1. Libraries and functions

1.1 Libraries

Load the required libraries.

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(sf)
Linking to GEOS 3.11.0, GDAL 3.5.3, PROJ 9.1.0; sf_use_s2() is TRUE
library(here)
here() starts at /Users/petermacpherson/Documents/Documents - Peter’s Mac mini/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr
library(readxl)
library(scales)

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
library(DT)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(brms)
Loading required package: Rcpp
Loading 'brms' package (version 2.21.0). Useful instructions
can be found by typing help('brms'). A more detailed introduction
to the package is available through vignette('brms_overview').

Attaching package: ‘brms’

The following object is masked from ‘package:stats’:

    ar
library(tidybayes)

Attaching package: ‘tidybayes’

The following objects are masked from ‘package:brms’:

    dstudent_t, pstudent_t, qstudent_t, rstudent_t
library(patchwork)
library(marginaleffects)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
library(ggrepel)
library(scico)
library(ggdensity)
library(ggpubr)
library(units)
udunits database from /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library/units/share/udunits/udunits2.xml
library(glue)
library(ggh4x)

Attaching package: ‘ggh4x’

The following object is masked from ‘package:ggplot2’:

    guide_axis_logticks

1.2 Helper functions

Functions that we will use throughout the script

#labeller for years
year_labels <- c(1950:1963)

#The Glasgow mass minuture chest X-ray campaign happened between 11th March and 12th April 1957
#Segment for graphs to match ACF period
acf_start <- decimal_date(ymd("1957-03-11"))
acf_end <- decimal_date(ymd("1957-04-12"))

Function for counterfactual plots



plot_counterfactual <- function(model_data, model, population_denominator, outcome, grouping_var=NULL, re_formula,...){
  
  #labeller for years
  year_labels <- c(1950:1964) #extra year for the extant of the x-axis

  #The Glasgow mass minuture chest X-ray campaign happened between 11th March and 12th April 1957
  #Segment for graphs to match ACF period
  acf_start <- decimal_date(ymd("1957-03-11"))
  acf_end <- decimal_date(ymd("1957-04-12"))

  summary <- {{model_data}} %>%
    select(year, year2, y_num, acf_period, {{population_denominator}}, {{outcome}}, {{grouping_var}}) %>%
    add_epred_draws({{model}}, re_formula={{re_formula}}) %>%
    group_by(year2, acf_period, {{grouping_var}}) %>%
    mean_qi() %>%
    mutate(.epred_inc = .epred/{{population_denominator}}*100000,
          .epred_inc.lower = .epred.lower/{{population_denominator}}*100000,
          .epred_inc.upper = .epred.upper/{{population_denominator}}*100000) %>%
    mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                  acf_period=="c. post-acf" ~ "Post Intervention"))



  #create the counterfactual (no intervention), and summarise
  
  counterfact <-
    add_epred_draws(object = {{model}},
                    newdata = {{model_data}} %>%
                                  select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, {{outcome}}) %>%
                                  mutate(acf_period = "a. pre-acf"), re_formula={{re_formula}}) %>%
    group_by(year2, acf_period, {{grouping_var}}) %>%
    mean_qi() %>%
    mutate(.epred_inc = .epred/{{population_denominator}}*100000,
         .epred_inc.lower = .epred.lower/{{population_denominator}}*100000,
         .epred_inc.upper = .epred.upper/{{population_denominator}}*100000) %>%
    mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention"))
  


  #plot the intervention effect
p <- summary %>%
    droplevels() %>%
    ggplot() +
    geom_vline(aes(xintercept=acf_start, linetype="Mass CXR screening intervention")) +
    geom_vline(aes(xintercept=acf_end, linetype="Mass CXR screening intervention")) +
    geom_ribbon(aes(ymin=.epred_inc.lower, ymax=.epred_inc.upper, x=year2, group = acf_period, fill=acf_period), alpha=0.5) +
    geom_ribbon(data = counterfact %>% filter(year>=1956), 
                aes(ymin=.epred_inc.lower, ymax=.epred_inc.upper, x=year2, fill="Counterfactual"), alpha=0.5) +
    geom_line(data = counterfact %>% filter(year>=1956), 
              aes(y=.epred_inc, x=year2, colour="Counterfactual")) +
    geom_line(aes(y=.epred_inc, x=year2, group=acf_period,  colour=acf_period)) +
    geom_point(data = {{model_data}} %>%
                 mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Pre-ACF",
                                               acf_period=="b. acf" ~ "ACF",
                                               acf_period=="c. post-acf" ~ "Post-ACF")), 
               aes(y={{outcome}}, x=year2, shape=fct_relevel(acf_period,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), size=2) +
    theme_grey() +
    scale_y_continuous(labels=comma, limits =c(0,400)) +
    scale_x_continuous(labels = year_labels,
                       breaks = year_labels) +
    scale_fill_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:", na.translate = F) +
    scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:", na.translate = F) +
    scale_shape_discrete(name="Empirical data (period):", na.translate = F) +
    scale_linetype_manual(values = 2, name="") +
    labs(
      x = "",
      y = "CNR (per 100,000)"
    ) +
    guides(x = "axis_truncated", y = "axis_truncated") +
    theme(legend.position = "bottom",
          legend.box="vertical", 
          text = element_text(size=10),
          axis.text.x = element_text(size=10, angle = 90, hjust=1, vjust=0.5),
          legend.text = element_text(size=10),
          legend.spacing.y = unit(0.1, 'cm'),
          axis.line = element_line(colour = "black")) 

    facet_vars <- vars(...)

  if (length(facet_vars) != 0) {
    p <- p + facet_wrap(facet_vars)
  }
  p

}

Function for calculating measures of change over time (RR.peak, RR.level, RR.slope)


summarise_change <- function(model_data, model, population_denominator, grouping_var = NULL, re_formula = NULL) {
  
  #functions for calculating RR.peak
  #i.e. relative case notification rate in 1957 vs. counterfactual trend for 1957
  
  grouping_var <- enquo(grouping_var)
  
  if (!is.null({{grouping_var}})) {
    
    #make the prediction matrix, conditional on whether we want random effects included or not.
    out <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num == 8),
                    acf_period = c("a. pre-acf", "b. acf")
    )
  } else {
    
    out <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num == 8),
                    acf_period = c("a. pre-acf", "b. acf")
    )
  }
  
  peak_draws <- add_epred_draws(newdata = out,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
    mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
    group_by(.draw, !!grouping_var) %>%
    summarise(estimate = last(epred_cnr)/first(epred_cnr)) %>%
    ungroup() %>%
    mutate(measure = "RR.peak")
  
  peak_summary <- peak_draws %>%
    group_by(!!grouping_var) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.peak")
  
  
  #functions for calculating RR.level
  #i.e. relative case notification rate in 1958 vs. counterfactual trend for 1958
  
    if (!is.null({{grouping_var}})) {
    out2 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num == 9),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  } else {
    
    out2 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num == 9),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  }
  
    level_draws <- add_epred_draws(newdata = out2,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
    arrange(y_num, .draw) %>%
    mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
    group_by(.draw, !!grouping_var) %>%
    summarise(estimate = last(epred_cnr)/first(epred_cnr)) %>%
    ungroup() %>%
    mutate(measure = "RR.level")
  
  level_summary <- level_draws %>%
    group_by(!!grouping_var) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.level")
    
    
  #functions for calculating RR.slope
  #i.e. relative change in case notification rate in 1958-1963 vs. counterfactual trend for 1959-1963
  
    if (!is.null({{grouping_var}})) {
    out3 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  } else {
    
    out3 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  }
  
    slope_draws <- add_epred_draws(newdata = out3,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
        arrange(y_num) %>%
        ungroup() %>%
        mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
        group_by(.draw, y_num, !!grouping_var) %>%
        summarise(slope = last(epred_cnr)/first(epred_cnr)) %>%
        ungroup() %>%
        group_by(.draw, !!grouping_var) %>%
        summarise(estimate = last(slope)/first(slope)) %>%
        mutate(measure = "RR.slope")
  
  slope_summary <- slope_draws %>%
     group_by(!!grouping_var) %>%
      mean_qi(estimate) %>%
      mutate(measure = "RR.slope")
    
  #gather all the results into a named list
    lst(peak_draws=peak_draws, peak_summary=peak_summary, 
        level_draws=level_draws, level_summary=level_summary, 
        slope_draws=slope_draws, slope_summary=slope_summary)
  
}

Function for calculating difference from counterfactual


calculate_counterfactual <- function(model_data, model, population_denominator, grouping_var=NULL, re_formula=NA){
  
  #effect vs. counterfactual
  counterfact <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}) %>%
                                    mutate(acf_period = "a. pre-acf"),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, year, {{grouping_var}}, acf_period) %>%
      mutate(.epred_inc_counterf = .epred/{{population_denominator}}*100000, .epred_counterf=.epred)  %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, {{population_denominator}}, .draw, .epred_counterf, .epred_inc_counterf, {{grouping_var}})
  
  #Calcuate case notification rate per draw, then summarise.
  post_change <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, acf_period),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, year, {{grouping_var}}, acf_period) %>%
      mutate(.epred_inc = .epred/{{population_denominator}}*100000)  %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, {{population_denominator}}, {{grouping_var}}, .draw, .epred, .epred_inc, {{grouping_var}}) 
  
  #for the overall period
    counterfact_overall <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}) %>%
                                    mutate(acf_period = "a. pre-acf"),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, {{grouping_var}}) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select({{population_denominator}}, .draw, .epred, {{grouping_var}})  %>%
      group_by(.draw, {{grouping_var}}) %>%
      summarise(.epred_counterf = sum(.epred)) 
  
  #Calcuate case notification rate per draw, then summarise.
  post_change_overall <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, acf_period),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, {{grouping_var}}) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select({{population_denominator}}, {{grouping_var}}, .draw, .epred) %>%
      group_by(.draw, {{grouping_var}}) %>%
      summarise(.epred = sum(.epred)) 
  
  
counter_post <-
  left_join(counterfact, post_change) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf,
           diff_inc100k = .epred_inc - .epred_inc_counterf,
           rr_inc100k = .epred_inc/.epred_inc_counterf) %>%
    group_by(year, {{grouping_var}}) %>%
    mean_qi(cases_averted, pct_change, diff_inc100k, rr_inc100k) %>%
    ungroup()

counter_post_overall <-
  left_join(counterfact_overall, post_change_overall) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf) %>%
    group_by({{grouping_var}}) %>%
    mean_qi(cases_averted, pct_change) %>%
    ungroup()

lst(counter_post, counter_post_overall)

}

Function for tidying up counterfactuals (mostly for making nice tables)


tidy_counterfactuals <- function(data){
  data %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(year = as.character(year),
            cases_averted = glue::glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change} ({pct_change.lower} to {pct_change.upper})"),
            diff_inc = glue::glue("{diff_inc100k} ({diff_inc100k.lower} to {diff_inc100k.upper})"),
            rr_inc = glue::glue("{rr_inc100k} ({rr_inc100k.lower} to {rr_inc100k.upper})"))
}


tidy_counterfactuals_overall <- function(data){
  data %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(year = as.character(year),
            cases_averted = glue::glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change} ({pct_change.lower} to {pct_change.upper})"))
}

2. Data

Import datasets for analysis

2.1 Shapefiles

Make a map of Glasgow wards


glasgow_wards_1951 <- st_read(here("mapping/glasgow_wards_1951.geojson"))
Reading layer `glasgow_wards_1951' from data source 
  `/Users/petermacpherson/Documents/Documents - Peter’s Mac mini/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr/mapping/glasgow_wards_1951.geojson' 
  using driver `GeoJSON'
Simple feature collection with 37 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -4.393502 ymin: 55.77464 xmax: -4.070411 ymax: 55.92814
Geodetic CRS:  WGS 84

#read in Scotland boundary
scotland <- st_read(here("mapping/Scotland_boundary/Scotland boundary.shp"))
Reading layer `Scotland boundary' from data source 
  `/Users/petermacpherson/Documents/Documents - Peter’s Mac mini/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr/mapping/Scotland_boundary/Scotland boundary.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 1 field
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 5513 ymin: 530249 xmax: 470332 ymax: 1220302
Projected CRS: OSGB36 / British National Grid
#make a bounding box for Glasgow
bbox <- st_bbox(glasgow_wards_1951) |> st_as_sfc()

#plot scotland with a bounding box around the City of Glasgow
scotland_with_bbox <- ggplot() +
  geom_sf(data = scotland, fill="antiquewhite") +
  geom_sf(data = bbox, colour = "#C60C30", fill="antiquewhite") +
  theme_void() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA, linewidth = 0.5),
        panel.background = element_rect(fill = "#EAF7FA", size = 0.3))
Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
ℹ Please use the `linewidth` argument instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
#plot the wards
#note we tidy up some names to fit on map
glasgow_ward_map <- glasgow_wards_1951 %>%
  mutate(ward = case_when(ward=="Shettleston and Tollcross" ~ "Shettleston and\nTollcross",
                          ward=="Partick (West)" ~ "Partick\n(West)",
                          ward=="Partick (East)" ~ "Partick\n(East)",
                          ward=="North Kelvin" ~ "North\nKelvin",
                          ward=="Kinning Park" ~ "Kinning\nPark",
                          TRUE ~ ward)) %>%
  
  ggplot() +
  geom_sf(aes(fill=division)) +
  geom_sf_label(aes(label = ward), size=3, fill=NA, label.size = NA, colour="black") +
  #scale_colour_identity() +
  scale_fill_brewer(palette = "Set3", name="City of Glasgow Division") +
  theme_grey() +
  labs(x="",
       y="",
       fill="Division") +
  theme(legend.position = "top",
        
        panel.border = element_rect(colour = "grey78", fill=NA, linewidth = 0.5),
        panel.background = element_rect(fill = "antiquewhite", size = 0.3),
        panel.grid.major = element_line(color = "grey78")) +
  guides(fill=guide_legend(title.position = "top", title.hjust = 0.5, title.theme = element_text(face="bold")))

#add the map of scotland as an inset
glasgow_ward_map + inset_element(scotland_with_bbox, 0.75, 0, 1, 0.4)
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data
ggsave(here("figures/s1.tiff"), height=10, width = 12)
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data

3. Denominators

Load in the datasets for denonomiators, and check for consistency.


overall_pops <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "overall_population")

overall_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()

#shift year to midpoint
overall_pops <- overall_pops %>%
  mutate(year2 = year+0.5)

Note, we have three population estimates:

  1. Population without institutionalised people or people in shipping
  2. Population in institutions
  3. Population in shipping

(Population in shipping is estimated from the 1951 census, so is the same for most years)

3.1 Overall population

First, plot the total population


overall_pops %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2), alpha=0.5, colour = "mediumseagreen", fill="mediumseagreen") +
  geom_point(aes(y=total_population, x=year2), colour = "mediumseagreen") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(
    title = "Glasgow Corporation: total population",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist()

NA
NA

Now the population excluding institutionalised and shipping population


overall_pops %>%
  ggplot() +
  geom_area(aes(y=population_without_inst_ship, x=year2), alpha=0.5, colour = "purple", fill="purple") +
  geom_point(aes(y=population_without_inst_ship, x=year2), colour = "purple") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(
    title = "Glasgow Corporation: population excluding institutionalised and shipping",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist()

NA
NA

3.2 Population by Ward

There are 5 Divisions containing 37 Wards in the Glasgow Corporation, with consistent boundaries over time.

#look-up table for divisions and wards
ward_lookup <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "divisions_wards")


ward_pops <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "ward_population")

ward_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()

#shift year to midpoint
ward_pops <- ward_pops %>%
  mutate(year2 = year+0.5)

#Get the Division population
division_pops <- ward_pops %>%
  group_by(division, year) %>%
  summarise(population_without_inst_ship = sum(population_without_inst_ship, na.rm = TRUE),
            institutions = sum(institutions, na.rm = TRUE),
            shipping = sum(shipping, na.rm = TRUE),
            total_population = sum(total_population, na.rm = TRUE))
`summarise()` has grouped output by 'division'. You can override using the `.groups` argument.
division_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA

Plot the overall population by Division and Ward


division_pops %>%
  mutate(year2 = year+0.5) %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2, colour=division, fill=division), alpha=0.8) +
  geom_point(aes(y=total_population, x=year2, colour=division)) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  facet_wrap(division~.) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_brewer(palette = "Set3", name = "") +
  scale_colour_brewer(palette = "Set3", name = "") +
  labs(
    title = "Glasgow Corporation: total population by Division",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA

ward_pops %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2, colour=division, fill=division)) +
  geom_point(aes(y=total_population, x=year2, colour=division), colour="black") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  facet_wrap(ward~., ncol=6) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_brewer(palette = "Set3", name="Division") +
  scale_colour_brewer(palette = "Set3", name = "Division") +
  labs(
    x = "",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

ggsave(here("figures/s2.tiff"), height=14, width=12)

Approximately, how many person-years of follow-up do we have?


overall_pops %>%
  ungroup() %>%
  summarise(across(year, length, .names = "years"),
            across(c(population_without_inst_ship, total_population), sum)) %>%
  mutate(across(where(is.double), comma)) %>%
  datatable()
NA
NA

Change in population by ward


ward_pops %>%
  group_by(ward) %>%
  summarise(pct_change_pop = (last(population_without_inst_ship) - first(population_without_inst_ship))/first(population_without_inst_ship)) %>%
  mutate(pct_change_pop = percent(pct_change_pop)) %>%
  arrange(pct_change_pop) %>%
  datatable()
NA
NA
NA

3.3 Population by age and sex


age_sex <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "age_sex_population") %>%
  pivot_longer(cols = c(male, female),
               names_to = "sex")

#collapse down to smaller age groups to be manageable
age_sex <- age_sex %>%
  ungroup() %>%
  mutate(age = case_when(age == "0 to 4" ~ "00 to 04",
                         age == "5 to 9" ~ "05 to 14",
                         age == "10 to 14" ~ "05 to 14",
                         age == "15 to 19" ~ "15 to 24",
                         age == "20 to 24" ~ "15 to 24",
                         age == "25 to 29" ~ "25 to 34",
                         age == "30 to 34" ~ "25 to 34",
                         age == "35 to 39" ~ "35 to 44",
                         age == "40 to 44" ~ "35 to 44",
                         age == "45 to 49" ~ "45 to 59",
                         age == "50 to 54" ~ "45 to 59",
                         age == "55 to 59" ~ "45 to 59",
                         TRUE ~ "60 & up")) %>%
  group_by(year, age, sex) %>%
  mutate(value = sum(value)) %>%
  ungroup()



m_age_sex <- lm(value ~ splines::ns(year, knots = 3)*age*sex, data = age_sex)

summary(m_age_sex)
Warning in summary.lm(m_age_sex) :
  essentially perfect fit: summary may be unreliable

Call:
lm(formula = value ~ splines::ns(year, knots = 3) * age * sex, 
    data = age_sex)

Residuals:
       Min         1Q     Median         3Q        Max 
-2.107e-10 -7.560e-13  0.000e+00  0.000e+00  2.107e-10 

Coefficients: (14 not defined because of singularities)
                                                    Estimate Std. Error    t value Pr(>|t|)    
(Intercept)                                        5.222e+04  3.820e-10  1.367e+14   <2e-16 ***
splines::ns(year, knots = 3)1                     -8.043e+03  7.621e-10 -1.055e+13   <2e-16 ***
splines::ns(year, knots = 3)2                             NA         NA         NA       NA    
age05 to 14                                        3.669e+04  4.679e-10  7.843e+13   <2e-16 ***
age15 to 24                                       -3.893e+03  4.679e-10 -8.320e+12   <2e-16 ***
age25 to 34                                       -3.996e+04  4.679e-10 -8.540e+13   <2e-16 ***
age35 to 44                                       -4.230e+04  4.679e-10 -9.040e+13   <2e-16 ***
age45 to 59                                        5.459e+04  4.411e-10  1.238e+14   <2e-16 ***
age60 & up                                         7.533e+04  4.126e-10  1.826e+14   <2e-16 ***
sexmale                                            3.374e+03  5.402e-10  6.244e+12   <2e-16 ***
splines::ns(year, knots = 3)1:age05 to 14         -1.863e+03  9.334e-10 -1.996e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age05 to 14                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age15 to 24          7.533e+04  9.334e-10  8.070e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age15 to 24                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age25 to 34          1.325e+05  9.334e-10  1.420e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age25 to 34                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age35 to 44          1.380e+05  9.334e-10  1.479e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age35 to 44                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age45 to 59          3.474e+03  8.800e-10  3.948e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age45 to 59                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age60 & up          -8.453e+04  8.232e-10 -1.027e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age60 & up                  NA         NA         NA       NA    
splines::ns(year, knots = 3)1:sexmale             -1.994e+03  1.078e-09 -1.850e+12   <2e-16 ***
splines::ns(year, knots = 3)2:sexmale                     NA         NA         NA       NA    
age05 to 14:sexmale                                1.053e+04  6.617e-10  1.592e+13   <2e-16 ***
age15 to 24:sexmale                                2.352e+04  6.617e-10  3.555e+13   <2e-16 ***
age25 to 34:sexmale                                1.355e+04  6.617e-10  2.047e+13   <2e-16 ***
age35 to 44:sexmale                               -1.727e+03  6.617e-10 -2.611e+12   <2e-16 ***
age45 to 59:sexmale                                2.774e+03  6.238e-10  4.446e+12   <2e-16 ***
age60 & up:sexmale                                -7.761e+04  5.835e-10 -1.330e+14   <2e-16 ***
splines::ns(year, knots = 3)1:age05 to 14:sexmale -2.049e+04  1.320e-09 -1.552e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age05 to 14:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age15 to 24:sexmale -6.780e+04  1.320e-09 -5.136e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age15 to 24:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age25 to 34:sexmale -3.804e+04  1.320e-09 -2.882e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age25 to 34:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age35 to 44:sexmale -1.171e+04  1.320e-09 -8.874e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age35 to 44:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age45 to 59:sexmale -3.473e+04  1.244e-09 -2.791e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age45 to 59:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age60 & up:sexmale   1.056e+05  1.164e-09  9.071e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age60 & up:sexmale          NA         NA         NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.755e-11 on 44 degrees of freedom
Multiple R-squared:      1, Adjusted R-squared:      1 
F-statistic: 1.714e+29 on 27 and 44 DF,  p-value: < 2.2e-16
age_levels <- age_sex %>% select(age) %>% distinct() %>% pull() 

age_sex_nd <- 
  crossing(
    age=age_levels,
    sex=c("male", "female"),
    year = 1950:1963
  )

pred_pops <- age_sex_nd %>% modelr::add_predictions(m_age_sex)
Warning in predict.lm(model, data) :
  prediction from rank-deficient fit; attr(*, "non-estim") has doubtful cases
pred_pops %>%
  ggplot(aes(x=year, y=pred, colour=age)) +
  geom_line() +
  geom_point() +
  facet_grid(sex~.) +
  scale_y_continuous(labels = comma, limits = c(0, 125000))


#How well do they match up with our overall populations?
pred_pops %>%
  group_by(year) %>%
  summarise(sum_pred_pop = sum(pred)) %>%
  right_join(overall_pops) %>%
  select(year, sum_pred_pop, population_without_inst_ship, total_population) %>%
  pivot_longer(cols = c(sum_pred_pop, population_without_inst_ship, total_population)) %>%
  ggplot(aes(x=year, y=value, colour=name)) +
  geom_point() +
  scale_y_continuous(labels = comma, limits = c(800000, 1250000))
Joining with `by = join_by(year)`

pred_pops %>%
  group_by(year, sex) %>%
  summarise(sum = sum(pred)) %>%
  group_by(year) %>%
  mutate(sex_ratio = first(sum)/last(sum))
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

Population pyramids


label_abs <- function(x) {
  comma(abs(x))
}


pred_pops %>%
  ungroup() %>%
  group_by(year) %>%
  mutate(year_pop = sum(pred),
         age_sex_pct = percent(pred/year_pop, accuracy=0.1)) %>%
  mutate(sex = case_when(sex=="male" ~ "Male",
                         sex=="female" ~ "Female")) %>%
  ggplot(
    aes(x = age, fill = sex, 
        y = ifelse(test = sex == "Female",yes = -pred, no = pred))) + 
  geom_bar(stat = "identity") +
  geom_text(aes(label = age_sex_pct),
            position= position_stack(vjust=0.5), colour="black", size=2.5) +
  facet_wrap(year~., ncol=7) +
  coord_flip() +
  scale_y_continuous(labels = label_abs) +
  scale_fill_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  theme_ggdist() +
  theme(axis.text.x = element_text(angle=90, hjust = 1, vjust=0.5),
        legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="", y="") 


ggsave(here("figures/s3.tiff"), width=10)
Saving 10 x 4.51 in image

Not perfect, but resonably good. But ahhhhh… the age groups don’t align with the case notification age groups! Come back to think about this later.

4. Tuberculosis cases

Import the tuberculosis cases dataset

4.1 Overall notifications

Overall, by year.


cases_by_year <- read_xlsx("2023-11-28_glasgow-acf.xlsx", sheet = "by_year")

cases_by_year%>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()


#shift year to midpoint
cases_by_year <- cases_by_year %>%
  mutate(year2 = year+0.5)

Plot the overall number of case notified per year, by pulmonary and extra pulmonary classification.


cases_by_year %>%
  select(-total_notifications, -year) %>%
  pivot_longer(cols = c(pulmonary_notifications, `non-pulmonary_notifications`)) %>%
  mutate(name = case_when(name == "pulmonary_notifications" ~ "Pulmonary TB",
                          name == "non-pulmonary_notifications" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=value, x=year2, group = name, fill=name), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA

4.2 Notifications by Division

Read in the datasets and merge together.


#list all the sheets
all_sheets <- excel_sheets("2023-11-28_glasgow-acf.xlsx")

#get the ward sheets
ward_sheets <- enframe(all_sheets) %>%
  filter(grepl("by_ward", value)) %>%
  pull(value)


cases_by_ward_sex_year <- map_df(ward_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

cases_by_ward_sex_year %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA

Aggregate together to get cases by division


cases_by_division <- cases_by_ward_sex_year %>%
  left_join(ward_lookup) %>%
  group_by(division, year, tb_type) %>%
  summarise(cases = sum(cases, na.rm = TRUE))
Joining with `by = join_by(ward)`
`summarise()` has grouped output by 'division', 'year'. You can override using the `.groups` argument.
#shift year to midpoint
cases_by_division <- cases_by_division %>%
  mutate(year2 = year+0.5) %>%
  ungroup()

cases_by_division  %>%
  select(-year2) %>%
  select(year, everything()) %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()


cases_by_division %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=cases, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(division~., scales = "free_y") +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications by Division",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

4.3 Notifications by ward



cases_by_ward <- cases_by_ward_sex_year %>%
  group_by(ward, year, tb_type) %>%
  summarise(cases = sum(cases, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'ward', 'year'. You can override using the `.groups` argument.
cases_by_ward %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  select(year, everything()) %>%
  datatable()

#shift year to midpoint
cases_by_ward <- cases_by_ward %>%
  mutate(year2 = year+0.5)

cases_by_ward %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=cases, x=year2, group = tb_type, fill=tb_type), alpha=0.8) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(ward~., scales = "free_y") +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications by Ward",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme(legend.position = "bottom")

NA
NA

4.4 Notifications by age and sex

As we don’t have denominators, we will just model the change in counts.


#list all the sheets
all_sheets <- excel_sheets("2023-11-28_glasgow-acf.xlsx")

#get the ward sheets
age_sex_sheets <- enframe(all_sheets) %>%
  filter(grepl("by_age_sex", value)) %>%
  pull(value)


cases_by_age_sex <- map_df(age_sex_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

cases_by_age_sex %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA
NA

4.5 Uptake of screening

What percentage of adults (15+ participated in the intervention in 1957)?

Note that in the Report of Sir Kenneth Cowan, we have the following estimates of participation (we will use these for the manuscript, as they are not based on my estimates)

male_adult_resident_participation <- 281875
female_adult_resident_participation <- 340474
male_adult_resident_population <- 381713
female_adult_resident_population <- 437588

#overall participation
(male_adult_resident_participation+female_adult_resident_participation)/(male_adult_resident_population+female_adult_resident_population)
[1] 0.7596097
#male participation
male_adult_resident_participation/male_adult_resident_population
[1] 0.7384475
#female participation
female_adult_resident_participation/female_adult_resident_population
[1] 0.7780698

Look at uptake of screening by age and sex

Combine figure with table for single figure.

ggsave(here("figures/s4.tiff"), height=10)
Saving 7 x 10 in image

Uptake by division


uptake_division <- read_xlsx("2024-03-26_mass_xray_uptake.xlsx", sheet = "uptake_division")

division_pops %>%
  filter(year==1957) %>%
  select(division, population_without_inst_ship) %>%
  left_join(uptake_division) %>%
  mutate(pct_pop_examined = examined/population_without_inst_ship)
Joining with `by = join_by(division)`

5 TB case notification rates

5.1 Overall TB case notification rates

Now calculate case notification rates per 100,000 population

Merge the notification and population denominator datasets together.

Here we need to include the whole population (with shipping and institutions) as they are included in the notifications.


overall_inc <- overall_pops %>%
  left_join(cases_by_year)
Joining with `by = join_by(year, year2)`
overall_inc <- overall_inc %>%
  mutate(inc_pulm_100k = pulmonary_notifications/total_population*100000,
         inc_ep_100k = `non-pulmonary_notifications`/total_population*100000,
         inc_100k = total_notifications/total_population*100000)

overall_inc %>%
  select(year, inc_100k, inc_pulm_100k, inc_ep_100k) %>%
  mutate_at(.vars = vars(inc_100k, inc_pulm_100k, inc_ep_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
ℹ Please use a list of either functions or lambdas:

# Simple named list: list(mean = mean, median = median)

# Auto named with `tibble::lst()`: tibble::lst(mean, median)

# Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

overall_inc %>%
  select(year2, inc_pulm_100k, inc_ep_100k) %>%
  pivot_longer(cols = c(inc_pulm_100k, `inc_ep_100k`)) %>%
  mutate(name = case_when(name == "inc_pulm_100k" ~ "Pulmonary TB",
                          name == "inc_ep_100k" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=value, x=year2, group = name, fill=name), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Case notification rate (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA
NA

Change in case notification rates pre-intervention

#pre-ACF
overall_inc %>%
  filter(year %in% 1950:1956) %>%
  summarise(change = (((last(inc_pulm_100k)-first(inc_pulm_100k))/first(inc_pulm_100k))/7)*100)

#post-ACF
overall_inc %>%
  filter(year %in% 1958:1963) %>%
  summarise(change = (((last(inc_pulm_100k)-first(inc_pulm_100k))/first(inc_pulm_100k))/6)*100)
NA

5.2 TB case notification rates by Division


division_inc <- division_pops %>%
  left_join(cases_by_division)
Joining with `by = join_by(division, year)`
division_inc <- division_inc %>%
  mutate(inc_100k = cases/total_population*100000)

division_inc %>%
  select(year, division, tb_type, inc_100k) %>%
  mutate_at(.vars = vars(inc_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
ℹ Please use a list of either functions or lambdas:

# Simple named list: list(mean = mean, median = median)

# Auto named with `tibble::lst()`: tibble::lst(mean, median)

# Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

division_inc %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=inc_100k, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(division~.) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate, by Division",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Case notification rate (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA
NA

5.2 TB case notification rates by Ward

Here we will filter out the institutions and harbour from the denominators, as we don’t have reliable population denominators for them.


ward_inc <- ward_pops %>%
  left_join(cases_by_ward)
Joining with `by = join_by(ward, year, year2)`
ward_inc <- ward_inc %>%
  mutate(inc_100k = cases/population_without_inst_ship*100000)

ward_inc %>%
  select(year, ward, tb_type, inc_100k) %>%
  mutate_at(.vars = vars(inc_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
ℹ Please use a list of either functions or lambdas:

# Simple named list: list(mean = mean, median = median)

# Auto named with `tibble::lst()`: tibble::lst(mean, median)

# Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

ward_inc %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=inc_100k, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(ward~.) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate, by Ward",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Incidence (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme(legend.position = "bottom")

NA
NA
NA
NA

On a map


st_as_sf(left_join(ward_inc, glasgow_wards_1951)) %>%
  filter(tb_type=="Pulmonary") %>%
  ggplot() +
  geom_sf(aes(fill=inc_100k)) +
  facet_wrap(year~., ncol = 7) +
  scale_fill_viridis_c(name="Case notification rate (per 100,000)",
                       option = "A") +
  theme_ggdist() +
  theme(legend.position = "top",
        legend.key.width = unit(2, "cm"),
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  guides(fill=guide_colorbar(title.position = "top"))
Joining with `by = join_by(division, ward, ward_number)`

6. TB Mortality

6.1 Overall Mortality

Import the TB mortality data.

First, overall deaths. Note that in the original reports, we have a pulmonary TB death rate per million for all years, and numbers of pulmonary TB deaths for each year apart from 1950.


#get the overall mortality sheets
deaths_sheets <- enframe(all_sheets) %>%
  filter(grepl("deaths", value)) %>%
  pull(value)


overall_deaths <- map_df(deaths_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

overall_deaths %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA
NA
NA

Plot the raw numbers of pulmonary deaths


overall_deaths %>%
  ggplot(aes(x=year, y=pulmonary_deaths)) +
  geom_line(colour = "#DE0D92") +
  geom_point(colour = "#DE0D92") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  labs(y="Pulmonary TB deaths per year",
       x = "Year",
       title = "Numbers of pulmonary TB deaths",
       subtitle = "Glasgow, 1950-1963",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: no data for 1950") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA))
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_point()`).

Now the incidence of pulmonary TB death

overall_deaths %>%
  ggplot(aes(x=year, y=pulmonary_death_rate_per_100k)) +
  geom_line(colour = "#4D6CFA") +
  geom_point(colour = "#4D6CFA") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(y="Annual incidence of death (per 100,000)",
       x = "Year",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA))

ggsave(here("figures/s8.tiff"), width=10)
Saving 10 x 4.51 in image

6. Table 1

Make Table 1 here, and save for publication.


overall_pops %>% 
  select(year, total_population) %>%
  left_join(overall_inc %>%
              select(year, 
                     pulmonary_notifications, inc_pulm_100k,
                     `non-pulmonary_notifications`, inc_ep_100k,
                     total_notifications, inc_100k)) %>%
  left_join(overall_deaths %>%
              select(year,
                     pulmonary_deaths, pulmonary_death_rate_per_100k)) %>%
  mutate(percent_pulmonary = percent(pulmonary_notifications/(total_notifications ), accuracy=0.1)) %>%
  mutate(across(where(is.numeric) & !(year),  ~round(., digits=1))) %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.)))
Joining with `by = join_by(year)`
Joining with `by = join_by(year)`

Comparison fo age-sex distribution of cases in 1950-1956 vs. 1957


label_abs2 <- function(x) {
  percent(abs(x))
}



cases_by_age_sex %>% 
  ungroup() %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(acf_period, age, sex) %>%
  summarise(cases = sum(cases)) %>%
  ungroup() %>%
  group_by(acf_period) %>%
  mutate(period_total = sum(cases)) %>%
  mutate(pct = cases/period_total) %>%
  mutate(pct2 = case_when(sex=="F" ~ -pct,
                          TRUE ~ pct)) %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
                 mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Pre-ACF",
                                               acf_period=="b. acf" ~ "ACF",
                                               acf_period=="c. post-acf" ~ "Post-ACF")) %>%
  ggplot() +
  geom_vline(aes(xintercept=0), linetype=2) +
  geom_point(aes(x=pct2,y=age, colour=fct_relevel(acf_period,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), stat="identity") +
  scale_x_continuous(labels=label_abs2, limits = c(-0.2, 0.2)) +
  scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA")) +
  theme_grey(base_family = "Aptos") +
  labs(x= "<- Female                Percent of cases              Male ->",
       y="") +
  theme(legend.title = element_blank(),
        legend.position = "bottom")
`summarise()` has grouped output by 'acf_period', 'age'. You can override using the `.groups` argument.
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)) :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
ggsave(here("figures/s5.tiff"))
Saving 7.29 x 4.51 in image

Prepare the datasets for modelling


mdata <- ward_inc %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(ward) %>%
  mutate(y_num = row_number()) %>%
  ungroup()


mdata_extrapulmonary <- ward_inc %>%
  filter(tb_type=="Non-Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(ward) %>%
  mutate(y_num = row_number()) %>%
  ungroup() %>% 
  filter(year<=1961) #no data for 1962 and 1963


#scaffold for overall predictions
overall_scaffold <- mdata %>%
    select(year, year2, y_num, acf_period, population_without_inst_ship, ward, cases) %>%
    group_by(year, year2, y_num, acf_period) %>%
    summarise(population_without_inst_ship = sum(population_without_inst_ship),
              cases = sum(cases)) %>%
    ungroup() %>%
    mutate(inc_100k = cases/population_without_inst_ship*100000) %>%
    left_join(mdata_extrapulmonary %>% group_by(year) %>%
                summarise(cases_extrapulmonary = sum(cases))) %>%
    mutate(inc_100k_extrapulmonary = cases_extrapulmonary/population_without_inst_ship*100000)
`summarise()` has grouped output by 'year', 'year2', 'y_num'. You can override using the `.groups` argument.
Joining with `by = join_by(year)`

7. Pulmonary TB model

7.1 Fit the model

Look at the mean and variance of counts (counts of pulmonary notifications are what we are predicting)


#Mean of counts per year
mean(mdata$cases)
[1] 48.32819
#variance of counts per year
var(mdata$cases)
[1] 915.5749

Quite a bit of over-dispersion here, so negative binomial distribution might be a better choice of distributional family than Poisson.

Fit the model with the data


m_pulmonary <- brm(
  cases ~ 0 + Intercept + y_num*acf_period + (1 + y_num*acf_period | ward) + offset(log(population_without_inst_ship)),
                  data = mdata,
                  family = negbinomial(),
                  seed = 1234,
                  chains = 4, cores = 4,
                  prior = prior(normal(0,1), class=b, coef = "Intercept") +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b) +
                          prior(exponential(1), class=sd) +
                          prior(lkj(4), class=cor),
  control = list(adapt_delta = 0.9))
Compiling Stan program...
Start sampling
starting worker pid=42892 on localhost:11039 at 13:53:33.579
starting worker pid=42905 on localhost:11039 at 13:53:33.668
starting worker pid=42918 on localhost:11039 at 13:53:33.758
starting worker pid=42931 on localhost:11039 at 13:53:33.847

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 0.000156 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 1.56 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 0.000154 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 1.54 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 0.000155 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 1.55 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 0.000162 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 1.62 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 2: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 3: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 1: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 4: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 3: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 2: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 1: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 4: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 3: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 1: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 4: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 3: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 1: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 4: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 2: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 2: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 3: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 3: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 1: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 1: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 4: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 4: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 2: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 3: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 1: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 4: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 2: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 3: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 1: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 4: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 2: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 3: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 1: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 4: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 2: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 3: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 1: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 4: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 2: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 22.133 seconds (Warm-up)
Chain 2:                17.498 seconds (Sampling)
Chain 2:                39.631 seconds (Total)
Chain 2: 
Chain 3: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 22.345 seconds (Warm-up)
Chain 3:                17.384 seconds (Sampling)
Chain 3:                39.729 seconds (Total)
Chain 3: 
Chain 1: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 23.301 seconds (Warm-up)
Chain 1:                17.586 seconds (Sampling)
Chain 1:                40.887 seconds (Total)
Chain 1: 
Chain 4: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 23.909 seconds (Warm-up)
Chain 4:                17.685 seconds (Sampling)
Chain 4:                41.594 seconds (Total)
Chain 4: 
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
  
#check model diagnostics
summary(m_pulmonary)
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ 0 + Intercept + y_num * acf_period + (1 + y_num * acf_period | ward) + offset(log(population_without_inst_ship)) 
   Data: mdata (Number of observations: 518) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Multilevel Hyperparameters:
~ward (Number of levels: 37) 
                                                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept)                                             0.25      0.04     0.19     0.33 1.00     1400     2056
sd(y_num)                                                 0.02      0.01     0.01     0.03 1.00      837      699
sd(acf_periodb.acf)                                       0.06      0.04     0.00     0.16 1.00     1546     1825
sd(acf_periodc.postMacf)                                  0.12      0.06     0.00     0.24 1.01      646      779
sd(y_num:acf_periodb.acf)                                 0.01      0.01     0.00     0.02 1.00     1234     1464
sd(y_num:acf_periodc.postMacf)                            0.01      0.01     0.00     0.02 1.01      422     1406
cor(Intercept,y_num)                                     -0.46      0.19    -0.76    -0.02 1.00     1781     1776
cor(Intercept,acf_periodb.acf)                           -0.22      0.29    -0.71     0.39 1.00     3134     2827
cor(y_num,acf_periodb.acf)                               -0.04      0.27    -0.55     0.50 1.00     5046     2574
cor(Intercept,acf_periodc.postMacf)                      -0.14      0.24    -0.58     0.36 1.00     4138     2791
cor(y_num,acf_periodc.postMacf)                           0.09      0.25    -0.39     0.56 1.00     2886     3023
cor(acf_periodb.acf,acf_periodc.postMacf)                 0.06      0.28    -0.47     0.58 1.00     1910     2495
cor(Intercept,y_num:acf_periodb.acf)                     -0.24      0.28    -0.71     0.35 1.00     2814     3021
cor(y_num,y_num:acf_periodb.acf)                         -0.05      0.27    -0.55     0.47 1.00     4863     3111
cor(acf_periodb.acf,y_num:acf_periodb.acf)               -0.06      0.28    -0.59     0.48 1.00     4367     3150
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)           0.07      0.27    -0.47     0.58 1.00     3450     2859
cor(Intercept,y_num:acf_periodc.postMacf)                -0.02      0.26    -0.51     0.48 1.00     3896     2794
cor(y_num,y_num:acf_periodc.postMacf)                    -0.03      0.27    -0.53     0.49 1.00     2307     2904
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)           0.04      0.28    -0.49     0.56 1.00     2463     2964
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)     -0.08      0.29    -0.62     0.49 1.00     2300     2996
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf)     0.06      0.28    -0.49     0.58 1.01     1916     2985

Regression Coefficients:
                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                     -6.13      0.05    -6.23    -6.04 1.01      815     1324
y_num                         -0.02      0.01    -0.03    -0.01 1.00     2151     2996
acf_periodb.acf                0.02      1.00    -1.98     1.99 1.00     3423     2951
acf_periodc.postMacf           0.04      0.10    -0.16     0.24 1.00     3399     3192
y_num:acf_periodb.acf          0.08      0.12    -0.16     0.33 1.00     3432     2931
y_num:acf_periodc.postMacf    -0.05      0.01    -0.07    -0.03 1.00     3212     3194

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape    92.51     20.80    61.39   141.90 1.00     2423     2772

Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_pulmonary)


pp_check(m_pulmonary, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

Nicer version of trace plots for supplemental material

as_draws_df(m_pulmonary) %>% 
  bayesplot::mcmc_rank_overlay(pars = vars(b_Intercept:shape),
             facet_args = list(ncol = 4)) +
  scale_colour_scico_d(palette = "managua", name = "Chain") +
  theme_ggdist()+
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        legend.position = "top")
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Nicer version of table of parameters for supplement


summarise_draws(m_pulmonary) %>%
  mutate(across(c(mean:ess_tail), comma, accuracy=0.01)) %>%
  write_csv(here("figures/s1_table.csv"))
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `across(c(mean:ess_tail), comma, accuracy = 0.01)`.
Caused by warning:
! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
Supply arguments directly to `.fns` through an anonymous function instead.

  # Previously
  across(a:b, mean, na.rm = TRUE)

  # Now
  across(a:b, \(x) mean(x, na.rm = TRUE))
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

7.2 Summarise change in CNRs

Summarise the posterior in graphical form


f1b <- plot_counterfactual(model_data = overall_scaffold, model = m_pulmonary, 
                           population_denominator = population_without_inst_ship, outcome = inc_100k, grouping_var=NULL,
                           re_formula = NA)
  
f1b
Warning: The S3 guide system was deprecated in ggplot2 3.5.0.
ℹ It has been replaced by a ggproto system that can be extended.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).

Make this into a figure combined with the map of empirical data


f1a <- st_as_sf(left_join(ward_inc, glasgow_wards_1951)) %>%
  filter(tb_type=="Pulmonary") %>%
  ggplot() +
  geom_sf(aes(fill=inc_100k), colour="grey98", lwd=0.01) +
  facet_wrap(year~., ncol = 7) +
  scale_fill_scico(name="CNR (per 100,000)",
                       palette = "acton", direction = -1) +
  theme_grey() +
  theme(legend.position = "top",
        #legend.key.width = unit(1, "cm"),
        legend.title.align = 0.5,
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.line = element_blank(),
        axis.ticks = element_blank(),
        panel.background = element_blank(),
        legend.title = element_text(size=10))
Joining with `by = join_by(division, ward, ward_number)`
Warning: The `legend.title.align` argument of `theme()` is deprecated as of ggplot2 3.5.0.
ℹ Please use theme(legend.title = element_text(hjust)) instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
(f1a / f1b) + plot_annotation(tag_levels = "A")
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).
ggsave(here("figures/f1.tiff"), width=7, height=8)
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).

Summary of change in notifications numerically


overall_change <- summarise_change(model_data=overall_scaffold, model=m_pulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=NULL, re_formula = NA)
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
overall_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA
NA

7.3 Compared to counterfactual

Numbers of pulmonary TB cases averted compared to counterfactual per year.


overall_pulmonary_counterf <- calculate_counterfactual(model_data = overall_scaffold, model=m_pulmonary, population_denominator = population_without_inst_ship)
Joining with `by = join_by(year, population_without_inst_ship, .draw)`
Joining with `by = join_by(.draw)`
overall_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Total pulmonary TB cases averted between 1958 and 1963


overall_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

7.4 Correlation between RR.peak, RR.level, and RR.slope

What are the correlations between peak, level, and slope?


#RR.peak histogram
a <- overall_change$peak_draws %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.peak",
       y="")

#RR. level histogram
b <- overall_change$level_draws  %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.level",
       y="")

#RR.slope histogram
c <- overall_change$slope_draws %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +  
  #scale_x_continuous(limits = c(0, 6)) +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.slope",
       y="")


#Correlation between RR.peak and RR.level
cor_rr_peak_rr_level <- round(cor(pluck(overall_change$peak_draws$estimate), pluck(overall_change$level_draws$estimate)), digits = 2)

#Correlation between RR.peak and RR.slope
cor_rr_peak_rr_slope <- round(cor(pluck(overall_change$peak_draws$estimate), pluck(overall_change$slope_draws$estimate)), digits = 2)

#Correlation between RR.level and RR.slope
cor_rr_level_rr_slope <- round(cor(pluck(overall_change$level_draws$estimate), pluck(overall_change$slope_draws$estimate)), digits = 2)


#plot of correlation between RR.peak and RR.level
d <- bind_cols(RR.peak=pluck(overall_change$peak_draws$estimate), 
          RR.level =pluck(overall_change$level_draws$estimate)) %>%
  ggplot(aes(y=RR.peak, x = RR.level)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=2.2, x=0.58, label=cor_rr_peak_rr_level), colour="firebrick")  +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))

#plot of correlation between RR.peak and RR.slope
e <- bind_cols(RR.peak=pluck(overall_change$peak_draws$estimate), 
          RR.slope =pluck(overall_change$slope_draws$estimate)) %>%
  ggplot(aes(y=RR.peak, x = RR.slope)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=2.1, x=0.65, label=cor_rr_peak_rr_slope), colour="firebrick")  +
  #scale_x_continuous(limits = c(0, 6)) +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))

#plot of correlation between RR.level and RR.slope
f <- bind_cols(RR.level=pluck(overall_change$level_draws$estimate), 
          RR.slope =pluck(overall_change$slope_draws$estimate)) %>%
  ggplot(aes(y=RR.level, x = RR.slope)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=0.75, x=0.65, label=cor_rr_level_rr_slope), colour="firebrick")  +  
  #scale_x_continuous(limits = c(0, 6)) +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))


(plot_spacer() + plot_spacer() + c) /
  (plot_spacer() + b + f) /
  (a + d + e)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning in geom_text(aes(y = 0.75, x = 0.65, label = cor_rr_level_rr_slope),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning in geom_text(aes(y = 2.2, x = 0.58, label = cor_rr_peak_rr_level),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
Warning in geom_text(aes(y = 2.1, x = 0.65, label = cor_rr_peak_rr_slope),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
ggsave(here("figures/s9.tiff"), width=8, height=8)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning in geom_text(aes(y = 0.75, x = 0.65, label = cor_rr_level_rr_slope),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning in geom_text(aes(y = 2.2, x = 0.58, label = cor_rr_peak_rr_level),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
Warning in geom_text(aes(y = 2.1, x = 0.65, label = cor_rr_peak_rr_slope),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'

7.5 Ward level pulmonary TB estimates

Plot the counterfactual at ward level


plot_counterfactual(model_data = mdata, model=m_pulmonary, outcome = inc_100k, population_denominator = population_without_inst_ship, 
                    grouping_var = ward, ward, re_formula= ~(1 + y_num*acf_period | ward)) +
  scale_y_continuous(limits = c(0,500))
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning: Removed 37 rows containing missing values or values outside the scale range (`geom_line()`).
  
ggsave(here("figures/s6.tiff"), width=16, height=12)
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning: Removed 37 rows containing missing values or values outside the scale range (`geom_line()`).

Summary of change in notifications at ward level


ward_change <- summarise_change(model_data=mdata, model=m_pulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=ward, 
                                   re_formula = ~(1 + y_num*acf_period | ward))
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw', 'y_num'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
ward_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()


#plot these in a figure
ward_effects <- ward_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  bind_rows(overall_change$peak_summary) %>%
  bind_rows(overall_change$level_summary) %>%
  bind_rows(overall_change$slope_summary) %>%
  mutate_at(.vars = vars(estimate:.upper), 
            .funs = funs(as.numeric)) %>%
  select(measure, everything()) %>%
  mutate(estimate = as.double(estimate)) %>%
  full_join(glasgow_wards_1951) %>% 
  mutate(ward2 = paste0(ward_number, ". ", ward)) %>%
  mutate(ward2 = case_when(is.na(ward) ~ "Overall",
                          TRUE ~ ward2)) %>%
  st_as_sf() 
Warning: `funs()` was deprecated in dplyr 0.8.0.
ℹ Please use a list of either functions or lambdas:

# Simple named list: list(mean = mean, median = median)

# Auto named with `tibble::lst()`: tibble::lst(mean, median)

# Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Joining with `by = join_by(ward)`
#function for plotting choropleth maps
plot_ward_effect <- function(data, measure){
  {{data}} %>%
  filter(measure == {{measure}}) %>%
  ggplot() +
  geom_sf(aes(fill=estimate)) +
  geom_sf_label(aes(label = ward_number), size=3, fill=NA, label.size = NA, colour="black") +
  scale_fill_scico(trans="log", palette = "roma", midpoint = 0, limits=c(0.5,2.25),
                       breaks = c(0.5, 0.75, 1, 1.5, 2, 2.5), labels = c(0.5, 0.75, 1, 1.5, 2, 2.5),
                   name="Relative rate") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        axis.text.x=element_text(angle=45, hjust=1)) +
    labs(x="", y="")
}

#function for plotting catapiller plots
plot_ward_cat <- function(data, measure, scale){

    ggplot() +
    geom_hline(aes(yintercept=1), linetype=2) +
    geom_pointrange(data = {{data}} %>%     
                      filter(measure=={{measure}}) %>%
                      filter(!is.na(ward)),
                    aes(y=estimate, ymin=.lower, ymax=.upper, 
                      x=fct_reorder(ward2, estimate), colour=estimate)) +
    geom_pointrange(data = {{data}} %>% 
                      filter(measure=={{measure}}) %>%
                      filter(is.na(ward)),
                    aes(y=estimate, ymin=.lower, ymax=.upper, 
                      x=ward2), colour="black") +
    coord_flip() +
    scale_colour_scico(trans="log", palette = "roma", midpoint = 0, limits=c(0.5,2.25), 
                       breaks = c(0.5, 0.75, 1, 1.5, 2, 2.5), labels = c(0.5, 0.75, 1, 1.5, 2, 2.5),
                       name="Relative rate") +
    scale_y_continuous() +
    theme_ggdist()  +
    theme(panel.border = element_rect(colour = "grey78", fill=NA)) +
    labs(x = "",
         y = "Relative rate (95% UI)")+
    guides(x = "axis_truncated", y = "axis_truncated")
}



ward_peak_i <- plot_ward_effect(data = ward_effects, measure = "RR.peak") + ggtitle("Peak effect")
ward_level_i <- plot_ward_effect(data = ward_effects, measure = "RR.level") + ggtitle("Level effect")
ward_slope_i <- plot_ward_effect(data = ward_effects, measure = "RR.slope") + ggtitle("Slope effect")

ward_peak_ii <- plot_ward_cat(data = ward_effects, measure = "RR.peak") + ggtitle("Peak effect")
ward_level_ii <- plot_ward_cat(data = ward_effects, measure = "RR.level") + ggtitle("Level effect")
ward_slope_ii <- plot_ward_cat(data = ward_effects, measure = "RR.slope") + ggtitle("Slope effect")

s4 <- (ward_peak_i + ward_level_i + ward_slope_i) /
  (ward_peak_ii + ward_level_ii + ward_slope_ii)

s4[[1]] <- s4[[1]] + plot_layout(tag_level = 'new')
s4[[2]] <- s4[[2]] + plot_layout(tag_level = 'new')
s4 + plot_annotation(tag_levels = c('A', '1')) + plot_layout(guides = 'collect') &
  theme(legend.position='bottom',
        legend.key.width = unit(3, "cm"))
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_label()`).
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_label()`).
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_label()`).
ggsave(here("figures/f2.tiff"), width = 16, height=12)
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_label()`).
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_label()`).
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_label()`).

Calculate the counterfactual per ward


ward_pulmonary_counterf <- calculate_counterfactual(model_data = mdata, model=m_pulmonary, 
                                                    population_denominator = population_without_inst_ship,
                                                    grouping_var = ward, re_formula=~(1 + y_num*acf_period | ward))
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
Joining with `by = join_by(year, population_without_inst_ship, .draw, ward)`
Joining with `by = join_by(.draw, ward)`
ward_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Overall counterfactual per ward


ward_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA

8. Extra-pulmonary TB notifications

Now we will model the extra-pulmonary TB notification rate. Struggling a bit with negative binomial model, so revert to Poisson.

8.1 Fit the model


m_extrapulmonary <- brm(
  cases ~ 1 + y_num*acf_period + (1 + y_num*acf_period | ward) + offset(log(population_without_inst_ship)),
                  data = mdata_extrapulmonary,
                  family = negbinomial(),
                  seed = 1234,
                  chains = 4, cores = 4,
                  prior = prior(normal(0,1000), class = Intercept) +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b) +
                          prior(exponential(1), class=sd) +
                          prior(lkj(2), class=cor))
Compiling Stan program...
Start sampling
starting worker pid=59760 on localhost:11039 at 16:32:19.174
starting worker pid=59773 on localhost:11039 at 16:32:19.265
starting worker pid=59786 on localhost:11039 at 16:32:19.355
starting worker pid=59799 on localhost:11039 at 16:32:19.443

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 0.000173 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 1.73 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 0.000158 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 1.58 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 0.000164 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 1.64 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 0.000161 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 1.61 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 1: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 2: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 3: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 4: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 1: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 2: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 3: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 4: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 1: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 3: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 1: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 4: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 3: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 1: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 1: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 4: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 2: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 2: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 3: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 3: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 4: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 4: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 1: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 2: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 3: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 2: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 3: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 1: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 4: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 2: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 3: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 1: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 2: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 3: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 4: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 1: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 2: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 8.734 seconds (Warm-up)
Chain 2:                3.992 seconds (Sampling)
Chain 2:                12.726 seconds (Total)
Chain 2: 
Chain 3: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 8.663 seconds (Warm-up)
Chain 3:                3.95 seconds (Sampling)
Chain 3:                12.613 seconds (Total)
Chain 3: 
Chain 1: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 8.614 seconds (Warm-up)
Chain 1:                4.872 seconds (Sampling)
Chain 1:                13.486 seconds (Total)
Chain 1: 
Chain 4: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 4: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 4: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 9.111 seconds (Warm-up)
Chain 4:                6.485 seconds (Sampling)
Chain 4:                15.596 seconds (Total)
Chain 4: 
Warning: There were 1 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
Warning: Examine the pairs() plot to diagnose sampling problems

Warning message:
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
package ‘StanHeaders’ was built under R version 4.3.3 
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
summary(m_extrapulmonary)
Warning: There were 1 divergent transitions after warmup. Increasing adapt_delta above 0.8 may help. See http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ 1 + y_num * acf_period + (1 + y_num * acf_period | ward) + offset(log(population_without_inst_ship)) 
   Data: mdata_extrapulmonary (Number of observations: 444) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Multilevel Hyperparameters:
~ward (Number of levels: 37) 
                                                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept)                                             0.33      0.06     0.23     0.46 1.00     1498     2238
sd(y_num)                                                 0.02      0.01     0.00     0.04 1.00      480     1177
sd(acf_periodb.acf)                                       0.11      0.09     0.01     0.32 1.00     2076     1877
sd(acf_periodc.postMacf)                                  0.13      0.09     0.01     0.35 1.01     1333     2181
sd(y_num:acf_periodb.acf)                                 0.01      0.01     0.00     0.04 1.00     1971     1580
sd(y_num:acf_periodc.postMacf)                            0.01      0.01     0.00     0.04 1.00     1101     1431
cor(Intercept,y_num)                                     -0.11      0.31    -0.66     0.51 1.00     2112     2441
cor(Intercept,acf_periodb.acf)                           -0.02      0.33    -0.64     0.60 1.00     4848     2867
cor(y_num,acf_periodb.acf)                               -0.01      0.33    -0.64     0.63 1.00     4347     3132
cor(Intercept,acf_periodc.postMacf)                      -0.08      0.32    -0.67     0.56 1.00     3794     2857
cor(y_num,acf_periodc.postMacf)                          -0.05      0.33    -0.68     0.58 1.00     2939     2782
cor(acf_periodb.acf,acf_periodc.postMacf)                 0.02      0.33    -0.61     0.64 1.00     2894     2908
cor(Intercept,y_num:acf_periodb.acf)                     -0.02      0.33    -0.64     0.61 1.00     4859     2790
cor(y_num,y_num:acf_periodb.acf)                         -0.01      0.33    -0.63     0.62 1.00     3994     3027
cor(acf_periodb.acf,y_num:acf_periodb.acf)               -0.07      0.34    -0.70     0.59 1.00     3289     3143
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)           0.01      0.33    -0.62     0.61 1.00     3534     3556
cor(Intercept,y_num:acf_periodc.postMacf)                -0.15      0.31    -0.69     0.51 1.00     3533     2900
cor(y_num,y_num:acf_periodc.postMacf)                    -0.06      0.33    -0.66     0.60 1.00     2552     3059
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)           0.03      0.34    -0.60     0.64 1.00     2220     3046
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)     -0.08      0.35    -0.72     0.61 1.00     2585     2729
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf)     0.03      0.34    -0.62     0.67 1.00     2197     2676

Regression Coefficients:
                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                     -7.93      0.07    -8.08    -7.79 1.00     1713     2651
y_num                         -0.09      0.01    -0.11    -0.07 1.00     4226     3004
acf_periodb.acf                0.05      0.99    -1.87     2.00 1.00     2042     2679
acf_periodc.postMacf          -0.35      0.39    -1.11     0.39 1.00     2385     2473
y_num:acf_periodb.acf         -0.02      0.12    -0.26     0.22 1.00     2041     2404
y_num:acf_periodc.postMacf     0.02      0.04    -0.06     0.10 1.00     2356     2564

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape    94.70     67.21    26.10   272.34 1.00     4055     3328

Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_extrapulmonary)

pp_check(m_extrapulmonary, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

8.2 Summary of change

Summarise in plot

plot_counterfactual(model_data = overall_scaffold %>% filter(year<=1961), model=m_extrapulmonary, 
                    population_denominator = population_without_inst_ship, outcome=inc_100k_extrapulmonary, re_formula = NA) +
  scale_y_continuous(limits = c(0,50))
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).
  
ggsave(here("figures/s10.tiff"), width=10)
Saving 10 x 4.51 in image
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).

Summarise numerically.


overall_change_extrapulmonary <- summarise_change(model_data=overall_scaffold, model=m_extrapulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=NULL, re_formula = NA)
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
overall_change_extrapulmonary %>%
  keep(names(.) %in% tokeep) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA

8.3 Compared to counterfactual

Numbers of extra-pulmonary TB cases averted overall.


overall_ep_counterf <- calculate_counterfactual(model_data = mdata_extrapulmonary, model=m_extrapulmonary, 
                                               population_denominator = population_without_inst_ship)
Joining with `by = join_by(year, population_without_inst_ship, .draw)`
Joining with `by = join_by(.draw)`
overall_ep_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA

Total extrapulmonary TB cases averted between 1958 and 1963


overall_ep_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Make into Table 2

bind_rows(
overall_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "PTB_ward"),

overall_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "PTB_overall"),

overall_ep_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "EPTB"),

overall_ep_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "EPTB overall")

) %>%
  select(model, year, diff_inc100k, diff_inc100k.lower:rr_inc100k.upper, 
         cases_averted:cases_averted.upper,
         pct_change:pct_change.upper) %>%
  transmute(model=model, year=year,
            diff_cnr = glue("{diff_inc100k} ({diff_inc100k.lower} to {diff_inc100k.upper})"),
            rr = glue("{rr_inc100k} ({rr_inc100k.lower} to {rr_inc100k.upper})"),
            cases_averted = glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue("{pct_change} ({pct_change.lower} to {pct_change.upper})")) %>%
  write_csv(here("figures/table2.csv"))

8.4 Ward-level extra-pulmonary summaries

Ward-level extra-pulmonary estimates in graphical form.


plot_counterfactual(model_data = mdata_extrapulmonary, model=m_extrapulmonary, outcome = inc_100k, 
                    population_denominator = population_without_inst_ship, grouping_var = ward,re_formula =~(y_num*acf_period | ward), 
                    ward) + scale_y_continuous(limits= c(0,75))
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
Warning: Removed 37 rows containing missing values or values outside the scale range (`geom_line()`).
  
ggsave(here("figures/s11.tiff"), width=10, height=12)
Warning: Removed 37 rows containing missing values or values outside the scale range (`geom_line()`).

Numerical summary.


ward_change_extrapulmonary <- summarise_change(model_data = mdata_extrapulmonary, model = m_extrapulmonary, 
                                population_denominator = population_without_inst_ship, grouping_var=ward,
                                re_formula = ~(y_num*acf_period | ward)) 
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw', 'y_num'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
ward_change_extrapulmonary  %>%
  keep(names(.) %in% tokeep) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA
NA
NA

9. Age-sex model

9.1 FIt the model

Fit the model

(Not rewritten the functions for this yet)


mdata_age_sex <- cases_by_age_sex %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  mutate(year2 = year+0.5) %>%
  group_by(age, sex) %>%
  mutate(y_num = row_number()) %>%
  ungroup()

m_age_sex <- brm(
  cases ~ y_num + (acf_period)*(age*sex) + (acf_period:y_num)*(age*sex),
                  data = mdata_age_sex,
                  family = negbinomial(),
                  seed = 1234,
                  chains = 4, cores = 4, 
                  prior = prior(normal(0,1), class = Intercept) +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b))
Compiling Stan program...
Start sampling
starting worker pid=59955 on localhost:11039 at 16:33:34.168
starting worker pid=59968 on localhost:11039 at 16:33:34.257
starting worker pid=59981 on localhost:11039 at 16:33:34.351
starting worker pid=59994 on localhost:11039 at 16:33:34.439

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 5.5e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.55 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 4.1e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.41 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 4.5e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.45 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 3.9e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.39 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 1: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 2: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 3: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 4: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 1: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 2: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 3: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 4: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 1: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 3: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 4: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 1: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 2: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 3: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 4: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 1: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 1: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 2: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 2: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 3: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 3: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 4: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 4: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 2: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 1: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 3: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 4: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 2: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 1: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 3: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 4: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 2: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 1: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 3: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 4: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 2: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 1: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 3: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 4: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 2: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 6.922 seconds (Warm-up)
Chain 2:                7.223 seconds (Sampling)
Chain 2:                14.145 seconds (Total)
Chain 2: 
Chain 1: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 6.971 seconds (Warm-up)
Chain 1:                7.951 seconds (Sampling)
Chain 1:                14.922 seconds (Total)
Chain 1: 
Chain 3: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 6.968 seconds (Warm-up)
Chain 3:                8.251 seconds (Sampling)
Chain 3:                15.219 seconds (Total)
Chain 3: 
Chain 4: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 7.126 seconds (Warm-up)
Chain 4:                8.202 seconds (Sampling)
Chain 4:                15.328 seconds (Total)
Chain 4: 
Warning message:
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
package ‘StanHeaders’ was built under R version 4.3.3 
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
Warning message:
package ‘StanHeaders’ was built under R version 4.3.3 
summary(m_age_sex)
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ y_num + (acf_period) * (age * sex) + (acf_period:y_num) * (age * sex) 
   Data: mdata_age_sex (Number of observations: 224) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Regression Coefficients:
                                         Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                                    4.42      0.12     4.19     4.64 1.00     1415     2552
y_num                                       -0.17      0.03    -0.23    -0.11 1.00     1302     2466
acf_periodb.acf                             -0.01      1.02    -2.04     2.00 1.00     7210     2491
acf_periodc.postMacf                        -0.49      0.33    -1.15     0.16 1.00     2357     2566
age06_15                                     0.63      0.15     0.33     0.94 1.00     2058     2788
age16_25                                     1.86      0.14     1.60     2.13 1.00     1655     2712
age26_35                                     1.13      0.14     0.85     1.40 1.00     1729     2902
age36_45                                     0.28      0.15    -0.02     0.57 1.00     2101     2790
age46_55                                    -0.62      0.18    -0.97    -0.28 1.00     2044     2768
age56_65                                    -1.07      0.19    -1.45    -0.71 1.00     2541     2475
age65P                                      -1.63      0.22    -2.06    -1.22 1.00     2849     2848
sexM                                         0.16      0.15    -0.14     0.46 1.00     1406     2375
age06_15:sexM                               -0.43      0.21    -0.83    -0.02 1.00     1953     2438
age16_25:sexM                               -0.58      0.18    -0.94    -0.22 1.00     1673     2923
age26_35:sexM                               -0.34      0.19    -0.70     0.03 1.00     1732     2640
age36_45:sexM                                0.24      0.20    -0.15     0.63 1.00     1860     2940
age46_55:sexM                                1.15      0.22     0.71     1.57 1.00     1801     2217
age56_65:sexM                                1.13      0.23     0.68     1.58 1.00     1997     2688
age65P:sexM                                  0.99      0.26     0.47     1.51 1.00     2488     2846
acf_periodb.acf:age06_15                     0.01      0.98    -1.90     1.92 1.00     7432     3008
acf_periodc.postMacf:age06_15               -0.59      0.52    -1.58     0.43 1.00     3652     3358
acf_periodb.acf:age16_25                     0.05      0.97    -1.83     1.95 1.00     6881     2982
acf_periodc.postMacf:age16_25                0.73      0.43    -0.10     1.58 1.00     3080     3224
acf_periodb.acf:age26_35                     0.04      0.95    -1.82     1.91 1.00     8078     3377
acf_periodc.postMacf:age26_35                0.66      0.43    -0.17     1.50 1.00     3365     3143
acf_periodb.acf:age36_45                     0.05      0.96    -1.86     1.91 1.00     6384     2598
acf_periodc.postMacf:age36_45                0.75      0.45    -0.15     1.62 1.00     3425     3123
acf_periodb.acf:age46_55                     0.06      0.96    -1.81     1.92 1.00     6486     3069
acf_periodc.postMacf:age46_55                0.85      0.48    -0.11     1.82 1.00     3452     2999
acf_periodb.acf:age56_65                     0.04      1.01    -1.95     2.04 1.00     7570     3142
acf_periodc.postMacf:age56_65                0.63      0.52    -0.37     1.64 1.00     3656     2661
acf_periodb.acf:age65P                       0.05      1.02    -1.95     1.99 1.00     7361     2833
acf_periodc.postMacf:age65P                  0.96      0.55    -0.12     2.06 1.00     3901     2909
acf_periodb.acf:sexM                        -0.01      0.99    -1.91     1.96 1.00     6593     2720
acf_periodc.postMacf:sexM                   -0.05      0.37    -0.78     0.69 1.00     2799     2989
y_num:acf_periodb.acf                       -0.10      0.13    -0.36     0.16 1.00     6943     2798
y_num:acf_periodc.postMacf                   0.04      0.04    -0.03     0.12 1.00     2115     2514
acf_periodb.acf:age06_15:sexM                0.01      0.99    -1.89     1.95 1.00     5773     3114
acf_periodc.postMacf:age06_15:sexM          -0.59      0.63    -1.82     0.66 1.00     4774     3262
acf_periodb.acf:age16_25:sexM                0.01      1.03    -2.00     2.05 1.00     6588     2695
acf_periodc.postMacf:age16_25:sexM           0.65      0.53    -0.37     1.70 1.00     3815     3544
acf_periodb.acf:age26_35:sexM                0.00      0.97    -1.87     1.91 1.00     6304     2724
acf_periodc.postMacf:age26_35:sexM           0.41      0.52    -0.61     1.43 1.00     4354     3045
acf_periodb.acf:age36_45:sexM               -0.00      0.96    -1.90     1.86 1.00     6501     3017
acf_periodc.postMacf:age36_45:sexM           0.10      0.55    -0.97     1.16 1.00     4082     3044
acf_periodb.acf:age46_55:sexM               -0.00      1.01    -1.97     2.04 1.00     6138     3169
acf_periodc.postMacf:age46_55:sexM           0.66      0.55    -0.41     1.75 1.00     3807     3054
acf_periodb.acf:age56_65:sexM                0.01      1.02    -1.96     2.02 1.00     5999     2750
acf_periodc.postMacf:age56_65:sexM           0.34      0.58    -0.80     1.45 1.00     4410     2601
acf_periodb.acf:age65P:sexM                 -0.02      1.00    -1.92     1.89 1.00     7236     3275
acf_periodc.postMacf:age65P:sexM             0.28      0.63    -0.93     1.51 1.00     4482     2948
y_num:acf_perioda.preMacf:age06_15           0.02      0.04    -0.05     0.10 1.00     1757     2661
y_num:acf_periodb.acf:age06_15               0.15      0.13    -0.10     0.41 1.00     6579     2817
y_num:acf_periodc.postMacf:age06_15          0.08      0.05    -0.02     0.17 1.00     3209     2988
y_num:acf_perioda.preMacf:age16_25           0.12      0.03     0.06     0.19 1.00     1487     2597
y_num:acf_periodb.acf:age16_25               0.24      0.13    -0.01     0.49 1.00     5988     3000
y_num:acf_periodc.postMacf:age16_25         -0.04      0.04    -0.12     0.04 1.00     2624     3146
y_num:acf_perioda.preMacf:age26_35           0.15      0.03     0.08     0.22 1.00     1508     2497
y_num:acf_periodb.acf:age26_35               0.31      0.12     0.07     0.55 1.00     7540     3386
y_num:acf_periodc.postMacf:age26_35          0.02      0.04    -0.06     0.10 1.00     2647     2940
y_num:acf_perioda.preMacf:age36_45           0.17      0.04     0.10     0.24 1.00     1676     2803
y_num:acf_periodb.acf:age36_45               0.40      0.13     0.16     0.65 1.00     5745     3142
y_num:acf_periodc.postMacf:age36_45          0.06      0.04    -0.02     0.14 1.00     2614     2787
y_num:acf_perioda.preMacf:age46_55           0.19      0.04     0.11     0.27 1.00     1816     2773
y_num:acf_periodb.acf:age46_55               0.44      0.13     0.19     0.69 1.00     5843     3198
y_num:acf_periodc.postMacf:age46_55          0.09      0.04     0.01     0.18 1.00     2820     2402
y_num:acf_perioda.preMacf:age56_65           0.17      0.05     0.08     0.26 1.00     2142     2731
y_num:acf_periodb.acf:age56_65               0.39      0.13     0.13     0.65 1.00     6828     2866
y_num:acf_periodc.postMacf:age56_65          0.11      0.05     0.02     0.20 1.00     3038     2886
y_num:acf_perioda.preMacf:age65P             0.23      0.05     0.13     0.32 1.00     2187     2906
y_num:acf_periodb.acf:age65P                 0.43      0.14     0.16     0.70 1.00     6816     2802
y_num:acf_periodc.postMacf:age65P            0.11      0.05     0.01     0.20 1.00     3106     2633
y_num:acf_perioda.preMacf:sexM               0.01      0.04    -0.06     0.09 1.00     1356     2575
y_num:acf_periodb.acf:sexM                  -0.04      0.14    -0.31     0.23 1.00     5779     2827
y_num:acf_periodc.postMacf:sexM             -0.01      0.04    -0.09     0.06 1.00     2093     2899
y_num:acf_perioda.preMacf:age06_15:sexM      0.00      0.05    -0.11     0.10 1.00     1861     2659
y_num:acf_periodb.acf:age06_15:sexM          0.04      0.14    -0.23     0.31 1.00     5277     2941
y_num:acf_periodc.postMacf:age06_15:sexM     0.10      0.06    -0.02     0.21 1.00     3700     3141
y_num:acf_perioda.preMacf:age16_25:sexM     -0.00      0.05    -0.09     0.09 1.00     1619     2848
y_num:acf_periodb.acf:age16_25:sexM          0.06      0.14    -0.22     0.35 1.00     5687     2961
y_num:acf_periodc.postMacf:age16_25:sexM    -0.01      0.05    -0.11     0.09 1.00     2988     3037
y_num:acf_perioda.preMacf:age26_35:sexM     -0.01      0.05    -0.10     0.08 1.00     1625     2712
y_num:acf_periodb.acf:age26_35:sexM          0.05      0.14    -0.22     0.33 1.00     5222     2964
y_num:acf_periodc.postMacf:age26_35:sexM    -0.01      0.05    -0.10     0.09 1.00     2960     2838
y_num:acf_perioda.preMacf:age36_45:sexM     -0.01      0.05    -0.11     0.08 1.00     1667     2795
y_num:acf_periodb.acf:age36_45:sexM          0.00      0.13    -0.26     0.26 1.00     5498     3062
y_num:acf_periodc.postMacf:age36_45:sexM    -0.00      0.05    -0.10     0.10 1.00     2981     2638
y_num:acf_perioda.preMacf:age46_55:sexM     -0.01      0.05    -0.11     0.10 1.00     1749     2303
y_num:acf_periodb.acf:age46_55:sexM         -0.00      0.14    -0.29     0.28 1.00     5539     2971
y_num:acf_periodc.postMacf:age46_55:sexM    -0.06      0.05    -0.17     0.04 1.00     2851     2957
y_num:acf_perioda.preMacf:age56_65:sexM      0.05      0.06    -0.06     0.16 1.00     1827     2578
y_num:acf_periodb.acf:age56_65:sexM          0.07      0.15    -0.22     0.36 1.00     5489     2712
y_num:acf_periodc.postMacf:age56_65:sexM     0.02      0.05    -0.09     0.12 1.00     3411     3130
y_num:acf_perioda.preMacf:age65P:sexM        0.01      0.06    -0.11     0.12 1.00     2108     2432
y_num:acf_periodb.acf:age65P:sexM            0.08      0.14    -0.19     0.36 1.00     6122     3176
y_num:acf_periodc.postMacf:age65P:sexM       0.01      0.06    -0.10     0.12 1.00     3429     2926

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape   203.04     67.43   107.48   371.91 1.00     2024     2752

Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_age_sex)

pp_check(m_age_sex, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

Summarise posterior


#posterior draws, and summarise
age_sex_summary <- mdata_age_sex %>%
  select(year, year2, y_num, acf_period, age, sex) %>%
  add_epred_draws(m_age_sex) %>%
  group_by(year2, acf_period, age, sex) %>%
  mean_qi() %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention"))

#create the counterfactual (no intervention), and summarise
age_sex_counterfact <- 
  tibble(year = mdata_age_sex$year,
         year2 = mdata_age_sex$year2,
         y_num = mdata_age_sex$y_num,
         age = mdata_age_sex$age,
         sex = mdata_age_sex$sex,
         acf_period = factor("a. pre-acf")) %>%
  add_epred_draws(m_age_sex) %>%
  group_by(year2, acf_period, age, sex) %>%
  mean_qi() %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention")) %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) 



age_sex_summary %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) %>%
  ggplot() +
  geom_ribbon(aes(ymin=.epred.lower, ymax=.epred.upper, x=year2, group = acf_period, fill=acf_period), alpha=0.5) +
  geom_ribbon(data = age_sex_counterfact %>% filter(year>=1956), 
              aes(ymin=.epred.lower, ymax=.epred.upper, x=year2, fill="Counterfactual"), alpha=0.5) +
  geom_line(data = age_sex_counterfact %>% filter(year>=1956), 
              aes(y=.epred, x=year2, colour="Counterfactual")) +
  geom_line(aes(y=.epred, x=year2, group=acf_period,  colour=acf_period)) +
  geom_point(data = mdata_age_sex %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="b. acf" ~ "Counterfactual",
                                  acf_period=="c. post-acf" ~ "Post Intervention"))%>%
                 mutate(acf_period2 = case_when(acf_period=="Before Intervention" ~ "Pre-ACF",
                                               acf_period=="Counterfactual" ~ "ACF",
                                               acf_period=="Post Intervention" ~ "Post-ACF")), 
  aes(y=cases, x=year2, shape=fct_relevel(acf_period2,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), size=2) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  facet_grid(fct_relevel(age, "65 & up y", "56 to 65y", "46 to 55y", "36 to 45y", "26 to 35y", "16 to 25y", "06 to 15y", "0 to 5y")~sex, 
             scales = "free_y") +
  theme_ggdist() +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:") +
  scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:") +
  scale_shape_discrete(name="Emprical data (period):", na.translate = F) +
  labs(
    x = "Year",
    y = "Case notifications (n)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme(legend.position = "bottom",          
        legend.box="vertical", 
        panel.border = element_rect(colour = "grey78", fill=NA))
  
ggsave(here("figures/s13.tiff"), height=10)
Saving 7.29 x 10 in image

9.2 Summary of impact of intervention

Calculate summary effects


#Peak
out_age_sex_1 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num == 8),
                      acf_period = c("a. pre-acf", "b. acf"))

peak_draws_age_sex <- add_epred_draws(newdata = out_age_sex_1,
                  object = m_age_sex) %>%
    group_by(.draw, age, sex) %>%
    summarise(estimate = last(.epred)/first(.epred)) %>%
    ungroup() %>%
    mutate(measure = "RR.peak")
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
peak_summary_age_sex <- peak_draws_age_sex %>%
    group_by(age, sex) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.peak")


#Level
 
out_age_sex_2 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num == 9),
                      acf_period = c("a. pre-acf", "c. post-acf"))
  
level_draws_age_sex <- add_epred_draws(newdata = out_age_sex_2,
                  object = m_age_sex) %>%
    arrange(y_num, .draw) %>%
    group_by(.draw, age, sex) %>%
    summarise(estimate = last(.epred)/first(.epred)) %>%
    ungroup() %>%
    mutate(measure = "RR.level")
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
level_summary_age_sex <- level_draws_age_sex %>%
    group_by(age, sex) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.level")

#Slope

out_age_sex_3 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf"))
  
slope_draws_age_sex <- add_epred_draws(newdata = out_age_sex_3,
                  object = m_age_sex) %>%
        arrange(y_num) %>%
        ungroup() %>%
        group_by(.draw, y_num, age, sex) %>%
        summarise(slope = last(.epred)/first(.epred)) %>%
        ungroup() %>%
        group_by(.draw, age, sex) %>%
        summarise(estimate = last(slope)/first(slope)) %>%
        mutate(measure = "RR.slope")
`summarise()` has grouped output by '.draw', 'y_num', 'age'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
slope_summary_age_sex <- slope_draws_age_sex %>%
     group_by(age, sex) %>%
      median_qi(estimate) %>%
      mutate(measure = "RR.slope")

Numerical summary of these summary results


bind_rows(
  peak_summary_age_sex, level_summary_age_sex, slope_summary_age_sex
) %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA
NA
NA

As a figure


peak_g_age_sex <- peak_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

#level plot
level_g_age_sex <- level_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

#slope plot
slope_g_age_sex <- slope_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

9.3 Compared to counterfactual


counterfact_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex) %>%
                                    mutate(acf_period = "a. pre-acf")) %>%
      filter(year>1957) %>%
      select(year, age, sex, .draw, .epred_counterf = .epred)
Adding missing grouping variables: `year2`, `y_num`, `acf_period`, `.row`
  
#Calcuate predicted number of cases per draw, then summarise.
post_change_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex, acf_period)) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, age, sex, .draw, .epred) 
  
#for the overall period
counterfact_overall_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex) %>%
                                    mutate(acf_period = "a. pre-acf")) %>%
      filter(year>1957) %>%
      select(age, sex, .draw, .epred)  %>%
      group_by(age, sex, .draw) %>%
      summarise(.epred_counterf = sum(.epred)) %>%
      mutate(year = "Overall (1958-1963)")
Adding missing grouping variables: `year`, `year2`, `y_num`, `acf_period`, `.row`
`summarise()` has grouped output by 'age', 'sex'. You can override using the `.groups` argument.
  
#Calcuate incidence per draw, then summarise.
post_change_overall_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex, acf_period)) %>%
      filter(year>1957) %>%
      select(age, sex, .draw, .epred) %>%
      group_by(.draw, age, sex) %>%
      summarise(.epred = sum(.epred)) 
Adding missing grouping variables: `year`, `year2`, `y_num`, `acf_period`, `.row`
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
counter_post_overall_age_sex <-
  left_join(counterfact_overall_age_sex, post_change_overall_age_sex) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf) %>%
    group_by(age, sex) %>%
    mean_qi(cases_averted, pct_change) %>%
    ungroup() %>%
    mutate(year = "Overall (1958-1963)") 
Joining with `by = join_by(age, sex, .draw)`
age_sex_txt <- counter_post_overall_age_sex %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  transmute(year = as.character(year),
            sex = sex,
            age = age,
            cases_averted = glue::glue("{cases_averted}\n({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change}\n({pct_change.lower} to {pct_change.upper})"))


age_sex_txt %>% datatable()
NA
NA

counterfactual_g_age_sex <- counter_post_overall_age_sex %>% 
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_pointrange(aes(x = age, y=cases_averted, ymin=cases_averted.lower, ymax=cases_averted.upper, colour=sex, shape=sex), position=position_dodge(width=0.5)) + 
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  scale_y_continuous(labels = comma) +
  labs(x="",
       y="Number (95% UI)",
       colour="") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        legend.position = "bottom")

counterfactual_g_age_sex

Join together for Figure 3.


(peak_g_age_sex + level_g_age_sex) / (slope_g_age_sex + counterfactual_g_age_sex) + plot_annotation(tag_levels = "A") + plot_layout(guides = "collect") & theme(legend.position = "bottom")

ggsave(here("figures/f3.tiff"), width = 12, height=8)

NA
NA
LS0tCnRpdGxlOiAiR2xhc2dvdyBUQiBBQ0YiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyAxLiBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKIyMjIyAxLjEgTGlicmFyaWVzCgpMb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMuCgpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShEVCkKbGlicmFyeShicm1zKQpsaWJyYXJ5KHRpZHliYXllcykKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkobWFyZ2luYWxlZmZlY3RzKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2NpY28pCmxpYnJhcnkoZ2dkZW5zaXR5KQpsaWJyYXJ5KGdncHVicikKbGlicmFyeSh1bml0cykKbGlicmFyeShnbHVlKQpsaWJyYXJ5KGdnaDR4KQoKYGBgCgojIyMjIDEuMiBIZWxwZXIgZnVuY3Rpb25zCgpGdW5jdGlvbnMgdGhhdCB3ZSB3aWxsIHVzZSB0aHJvdWdob3V0IHRoZSBzY3JpcHQKCmBgYHtyfQojbGFiZWxsZXIgZm9yIHllYXJzCnllYXJfbGFiZWxzIDwtIGMoMTk1MDoxOTYzKQoKI1RoZSBHbGFzZ293IG1hc3MgbWludXR1cmUgY2hlc3QgWC1yYXkgY2FtcGFpZ24gaGFwcGVuZWQgYmV0d2VlbiAxMXRoIE1hcmNoIGFuZCAxMnRoIEFwcmlsIDE5NTcKI1NlZ21lbnQgZm9yIGdyYXBocyB0byBtYXRjaCBBQ0YgcGVyaW9kCmFjZl9zdGFydCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTAzLTExIikpCmFjZl9lbmQgPC0gZGVjaW1hbF9kYXRlKHltZCgiMTk1Ny0wNC0xMiIpKQoKCmBgYAoKRnVuY3Rpb24gZm9yIGNvdW50ZXJmYWN0dWFsIHBsb3RzCgpgYGB7cn0KCgpwbG90X2NvdW50ZXJmYWN0dWFsIDwtIGZ1bmN0aW9uKG1vZGVsX2RhdGEsIG1vZGVsLCBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yLCBvdXRjb21lLCBncm91cGluZ192YXI9TlVMTCwgcmVfZm9ybXVsYSwuLi4pewogIAogICNsYWJlbGxlciBmb3IgeWVhcnMKICB5ZWFyX2xhYmVscyA8LSBjKDE5NTA6MTk2NCkgI2V4dHJhIHllYXIgZm9yIHRoZSBleHRhbnQgb2YgdGhlIHgtYXhpcwoKICAjVGhlIEdsYXNnb3cgbWFzcyBtaW51dHVyZSBjaGVzdCBYLXJheSBjYW1wYWlnbiBoYXBwZW5lZCBiZXR3ZWVuIDExdGggTWFyY2ggYW5kIDEydGggQXByaWwgMTk1NwogICNTZWdtZW50IGZvciBncmFwaHMgdG8gbWF0Y2ggQUNGIHBlcmlvZAogIGFjZl9zdGFydCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTAzLTExIikpCiAgYWNmX2VuZCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTA0LTEyIikpCgogIHN1bW1hcnkgPC0ge3ttb2RlbF9kYXRhfX0gJT4lCiAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tvdXRjb21lfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgYWRkX2VwcmVkX2RyYXdzKHt7bW9kZWx9fSwgcmVfZm9ybXVsYT17e3JlX2Zvcm11bGF9fSkgJT4lCiAgICBncm91cF9ieSh5ZWFyMiwgYWNmX3BlcmlvZCwge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICBtZWFuX3FpKCkgJT4lCiAgICBtdXRhdGUoLmVwcmVkX2luYyA9IC5lcHJlZC97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDAsCiAgICAgICAgICAuZXByZWRfaW5jLmxvd2VyID0gLmVwcmVkLmxvd2VyL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwKICAgICAgICAgIC5lcHJlZF9pbmMudXBwZXIgPSAuZXByZWQudXBwZXIve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwKSAlPiUKICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJCZWZvcmUgSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkKCgoKICAjY3JlYXRlIHRoZSBjb3VudGVyZmFjdHVhbCAobm8gaW50ZXJ2ZW50aW9uKSwgYW5kIHN1bW1hcmlzZQogIAogIGNvdW50ZXJmYWN0IDwtCiAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0ge3ttb2RlbH19LAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB7e2dyb3VwaW5nX3Zhcn19LCB7e291dGNvbWV9fSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksIHJlX2Zvcm11bGE9e3tyZV9mb3JtdWxhfX0pICU+JQogICAgZ3JvdXBfYnkoeWVhcjIsIGFjZl9wZXJpb2QsIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgbWVhbl9xaSgpICU+JQogICAgbXV0YXRlKC5lcHJlZF9pbmMgPSAuZXByZWQve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwLAogICAgICAgICAuZXByZWRfaW5jLmxvd2VyID0gLmVwcmVkLmxvd2VyL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwKICAgICAgICAgLmVwcmVkX2luYy51cHBlciA9IC5lcHJlZC51cHBlci97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDApICU+JQogICAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkKICAKCgogICNwbG90IHRoZSBpbnRlcnZlbnRpb24gZWZmZWN0CnAgPC0gc3VtbWFyeSAlPiUKICAgIGRyb3BsZXZlbHMoKSAlPiUKICAgIGdncGxvdCgpICsKICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0LCBsaW5ldHlwZT0iTWFzcyBDWFIgc2NyZWVuaW5nIGludGVydmVudGlvbiIpKSArCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQsIGxpbmV0eXBlPSJNYXNzIENYUiBzY3JlZW5pbmcgaW50ZXJ2ZW50aW9uIikpICsKICAgIGdlb21fcmliYm9uKGFlcyh5bWluPS5lcHJlZF9pbmMubG93ZXIsIHltYXg9LmVwcmVkX2luYy51cHBlciwgeD15ZWFyMiwgZ3JvdXAgPSBhY2ZfcGVyaW9kLCBmaWxsPWFjZl9wZXJpb2QpLCBhbHBoYT0wLjUpICsKICAgIGdlb21fcmliYm9uKGRhdGEgPSBjb3VudGVyZmFjdCAlPiUgZmlsdGVyKHllYXI+PTE5NTYpLCAKICAgICAgICAgICAgICAgIGFlcyh5bWluPS5lcHJlZF9pbmMubG93ZXIsIHltYXg9LmVwcmVkX2luYy51cHBlciwgeD15ZWFyMiwgZmlsbD0iQ291bnRlcmZhY3R1YWwiKSwgYWxwaGE9MC41KSArCiAgICBnZW9tX2xpbmUoZGF0YSA9IGNvdW50ZXJmYWN0ICU+JSBmaWx0ZXIoeWVhcj49MTk1NiksIAogICAgICAgICAgICAgIGFlcyh5PS5lcHJlZF9pbmMsIHg9eWVhcjIsIGNvbG91cj0iQ291bnRlcmZhY3R1YWwiKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5PS5lcHJlZF9pbmMsIHg9eWVhcjIsIGdyb3VwPWFjZl9wZXJpb2QsICBjb2xvdXI9YWNmX3BlcmlvZCkpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHt7bW9kZWxfZGF0YX19ICU+JQogICAgICAgICAgICAgICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJQcmUtQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iYi4gYWNmIiB+ICJBQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdC1BQ0YiKSksIAogICAgICAgICAgICAgICBhZXMoeT17e291dGNvbWV9fSwgeD15ZWFyMiwgc2hhcGU9ZmN0X3JlbGV2ZWwoYWNmX3BlcmlvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcmUtQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvc3QtQUNGIikpLCBzaXplPTIpICsKICAgIHRoZW1lX2dyZXkoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hLCBsaW1pdHMgPWMoMCw0MDApKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9zaGFwZV9kaXNjcmV0ZShuYW1lPSJFbXBpcmljYWwgZGF0YSAocGVyaW9kKToiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gMiwgbmFtZT0iIikgKwogICAgbGFicygKICAgICAgeCA9ICIiLAogICAgICB5ID0gIkNOUiAocGVyIDEwMCwwMDApIgogICAgKSArCiAgICBndWlkZXMoeCA9ICJheGlzX3RydW5jYXRlZCIsIHkgPSAiYXhpc190cnVuY2F0ZWQiKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgIGxlZ2VuZC5ib3g9InZlcnRpY2FsIiwgCiAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgYW5nbGUgPSA5MCwgaGp1c3Q9MSwgdmp1c3Q9MC41KSwKICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMC4xLCAnY20nKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSkgCgogICAgZmFjZXRfdmFycyA8LSB2YXJzKC4uLikKCiAgaWYgKGxlbmd0aChmYWNldF92YXJzKSAhPSAwKSB7CiAgICBwIDwtIHAgKyBmYWNldF93cmFwKGZhY2V0X3ZhcnMpCiAgfQogIHAKCn0KCmBgYAoKRnVuY3Rpb24gZm9yIGNhbGN1bGF0aW5nICBtZWFzdXJlcyBvZiBjaGFuZ2Ugb3ZlciB0aW1lIChSUi5wZWFrLCBSUi5sZXZlbCwgUlIuc2xvcGUpCgoKYGBge3J9CgpzdW1tYXJpc2VfY2hhbmdlIDwtIGZ1bmN0aW9uKG1vZGVsX2RhdGEsIG1vZGVsLCBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yLCBncm91cGluZ192YXIgPSBOVUxMLCByZV9mb3JtdWxhID0gTlVMTCkgewogIAogICNmdW5jdGlvbnMgZm9yIGNhbGN1bGF0aW5nIFJSLnBlYWsKICAjaS5lLiByZWxhdGl2ZSBjYXNlIG5vdGlmaWNhdGlvbiByYXRlIGluIDE5NTcgdnMuIGNvdW50ZXJmYWN0dWFsIHRyZW5kIGZvciAxOTU3CiAgCiAgZ3JvdXBpbmdfdmFyIDwtIGVucXVvKGdyb3VwaW5nX3ZhcikKICAKICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIAogICAgI21ha2UgdGhlIHByZWRpY3Rpb24gbWF0cml4LCBjb25kaXRpb25hbCBvbiB3aGV0aGVyIHdlIHdhbnQgcmFuZG9tIGVmZmVjdHMgaW5jbHVkZWQgb3Igbm90LgogICAgb3V0IDwtIGNyb3NzaW5nKHt7bW9kZWxfZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgeV9udW0sICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA4KSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJiLiBhY2YiKQogICAgKQogIH0gZWxzZSB7CiAgICAKICAgIG91dCA8LSBjcm9zc2luZyh7e21vZGVsX2RhdGF9fSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoe3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHlfbnVtKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA4KSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJiLiBhY2YiKQogICAgKQogIH0KICAKICBwZWFrX2RyYXdzIDwtIGFkZF9lcHJlZF9kcmF3cyhuZXdkYXRhID0gb3V0LAogICAgICAgICAgICAgICAgICBvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICBtdXRhdGUoZXByZWRfY25yID0gLmVwcmVkL3BvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAqMTAwMDAwKSAlPiUKICAgIGdyb3VwX2J5KC5kcmF3LCAhIWdyb3VwaW5nX3ZhcikgJT4lCiAgICBzdW1tYXJpc2UoZXN0aW1hdGUgPSBsYXN0KGVwcmVkX2NucikvZmlyc3QoZXByZWRfY25yKSkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5wZWFrIikKICAKICBwZWFrX3N1bW1hcnkgPC0gcGVha19kcmF3cyAlPiUKICAgIGdyb3VwX2J5KCEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgIG1lYW5fcWkoZXN0aW1hdGUpICU+JQogICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIucGVhayIpCiAgCiAgCiAgI2Z1bmN0aW9ucyBmb3IgY2FsY3VsYXRpbmcgUlIubGV2ZWwKICAjaS5lLiByZWxhdGl2ZSBjYXNlIG5vdGlmaWNhdGlvbiByYXRlIGluIDE5NTggdnMuIGNvdW50ZXJmYWN0dWFsIHRyZW5kIGZvciAxOTU4CiAgCiAgICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIG91dDIgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikKICAgICkKICB9IGVsc2UgewogICAgCiAgICBvdXQyIDwtIGNyb3NzaW5nKHt7bW9kZWxfZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgeV9udW0pICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikKICAgICkKICB9CiAgCiAgICBsZXZlbF9kcmF3cyA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dDIsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IHt7cmVfZm9ybXVsYX19KSAlPiUKICAgIGFycmFuZ2UoeV9udW0sIC5kcmF3KSAlPiUKICAgIG11dGF0ZShlcHJlZF9jbnIgPSAuZXByZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoZXByZWRfY25yKS9maXJzdChlcHJlZF9jbnIpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKICAKICBsZXZlbF9zdW1tYXJ5IDwtIGxldmVsX2RyYXdzICU+JQogICAgZ3JvdXBfYnkoISFncm91cGluZ192YXIpICU+JQogICAgbWVhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5sZXZlbCIpCiAgICAKICAgIAogICNmdW5jdGlvbnMgZm9yIGNhbGN1bGF0aW5nIFJSLnNsb3BlCiAgI2kuZS4gcmVsYXRpdmUgY2hhbmdlIGluIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgaW4gMTk1OC0xOTYzIHZzLiBjb3VudGVyZmFjdHVhbCB0cmVuZCBmb3IgMTk1OS0xOTYzCiAgCiAgICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIG91dDMgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtICVpbiUgYyg5LDE0KSksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYy4gcG9zdC1hY2YiKQogICAgKQogIH0gZWxzZSB7CiAgICAKICAgIG91dDMgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeV9udW0gJWluJSBjKDksMTQpKSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJjLiBwb3N0LWFjZiIpCiAgICApCiAgfQogIAogICAgc2xvcGVfZHJhd3MgPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXQzLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgICAgYXJyYW5nZSh5X251bSkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIG11dGF0ZShlcHJlZF9jbnIgPSAuZXByZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApICU+JQogICAgICAgIGdyb3VwX2J5KC5kcmF3LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgIHN1bW1hcmlzZShzbG9wZSA9IGxhc3QoZXByZWRfY25yKS9maXJzdChlcHJlZF9jbnIpKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgZ3JvdXBfYnkoLmRyYXcsICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgICBzdW1tYXJpc2UoZXN0aW1hdGUgPSBsYXN0KHNsb3BlKS9maXJzdChzbG9wZSkpICU+JQogICAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKICAKICBzbG9wZV9zdW1tYXJ5IDwtIHNsb3BlX2RyYXdzICU+JQogICAgIGdyb3VwX2J5KCEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgbWVhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKICAgIAogICNnYXRoZXIgYWxsIHRoZSByZXN1bHRzIGludG8gYSBuYW1lZCBsaXN0CiAgICBsc3QocGVha19kcmF3cz1wZWFrX2RyYXdzLCBwZWFrX3N1bW1hcnk9cGVha19zdW1tYXJ5LCAKICAgICAgICBsZXZlbF9kcmF3cz1sZXZlbF9kcmF3cywgbGV2ZWxfc3VtbWFyeT1sZXZlbF9zdW1tYXJ5LCAKICAgICAgICBzbG9wZV9kcmF3cz1zbG9wZV9kcmF3cywgc2xvcGVfc3VtbWFyeT1zbG9wZV9zdW1tYXJ5KQogIAp9CgpgYGAKCgpGdW5jdGlvbiBmb3IgY2FsY3VsYXRpbmcgZGlmZmVyZW5jZSBmcm9tIGNvdW50ZXJmYWN0dWFsCgpgYGB7cn0KCmNhbGN1bGF0ZV9jb3VudGVyZmFjdHVhbCA8LSBmdW5jdGlvbihtb2RlbF9kYXRhLCBtb2RlbCwgcG9wdWxhdGlvbl9kZW5vbWluYXRvciwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGE9TkEpewogIAogICNlZmZlY3QgdnMuIGNvdW50ZXJmYWN0dWFsCiAgY291bnRlcmZhY3QgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksCiAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0ge3tyZV9mb3JtdWxhfX0pICU+JQogICAgICBncm91cF9ieSguZHJhdywgeWVhciwge3tncm91cGluZ192YXJ9fSwgYWNmX3BlcmlvZCkgJT4lCiAgICAgIG11dGF0ZSguZXByZWRfaW5jX2NvdW50ZXJmID0gLmVwcmVkL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwgLmVwcmVkX2NvdW50ZXJmPS5lcHJlZCkgICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3QoeWVhciwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIC5kcmF3LCAuZXByZWRfY291bnRlcmYsIC5lcHJlZF9pbmNfY291bnRlcmYsIHt7Z3JvdXBpbmdfdmFyfX0pCiAgCiAgI0NhbGN1YXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgogIHBvc3RfY2hhbmdlIDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0ge3ttb2RlbF9kYXRhfX0gJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB7e2dyb3VwaW5nX3Zhcn19LCBhY2ZfcGVyaW9kKSwKICAgICAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCB5ZWFyLCB7e2dyb3VwaW5nX3Zhcn19LCBhY2ZfcGVyaW9kKSAlPiUKICAgICAgbXV0YXRlKC5lcHJlZF9pbmMgPSAuZXByZWQve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwKSAgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIHNlbGVjdCh5ZWFyLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tncm91cGluZ192YXJ9fSwgLmRyYXcsIC5lcHJlZCwgLmVwcmVkX2luYywge3tncm91cGluZ192YXJ9fSkgCiAgCiAgI2ZvciB0aGUgb3ZlcmFsbCBwZXJpb2QKICAgIGNvdW50ZXJmYWN0X292ZXJhbGwgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksCiAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0ge3tyZV9mb3JtdWxhfX0pICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgLmRyYXcsIC5lcHJlZCwge3tncm91cGluZ192YXJ9fSkgICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWRfY291bnRlcmYgPSBzdW0oLmVwcmVkKSkgCiAgCiAgI0NhbGN1YXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgogIHBvc3RfY2hhbmdlX292ZXJhbGwgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0sIGFjZl9wZXJpb2QpLAogICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IHt7cmVfZm9ybXVsYX19KSAlPiUKICAgICAgZ3JvdXBfYnkoLmRyYXcsIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3Qoe3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0sIC5kcmF3LCAuZXByZWQpICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWQgPSBzdW0oLmVwcmVkKSkgCiAgCiAgCmNvdW50ZXJfcG9zdCA8LQogIGxlZnRfam9pbihjb3VudGVyZmFjdCwgcG9zdF9jaGFuZ2UpICU+JQogICAgbXV0YXRlKGNhc2VzX2F2ZXJ0ZWQgPSAuZXByZWRfY291bnRlcmYtLmVwcmVkLAogICAgICAgICAgIHBjdF9jaGFuZ2UgPSAoLmVwcmVkIC0gLmVwcmVkX2NvdW50ZXJmKS8uZXByZWRfY291bnRlcmYsCiAgICAgICAgICAgZGlmZl9pbmMxMDBrID0gLmVwcmVkX2luYyAtIC5lcHJlZF9pbmNfY291bnRlcmYsCiAgICAgICAgICAgcnJfaW5jMTAwayA9IC5lcHJlZF9pbmMvLmVwcmVkX2luY19jb3VudGVyZikgJT4lCiAgICBncm91cF9ieSh5ZWFyLCB7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgIG1lYW5fcWkoY2FzZXNfYXZlcnRlZCwgcGN0X2NoYW5nZSwgZGlmZl9pbmMxMDBrLCBycl9pbmMxMDBrKSAlPiUKICAgIHVuZ3JvdXAoKQoKY291bnRlcl9wb3N0X292ZXJhbGwgPC0KICBsZWZ0X2pvaW4oY291bnRlcmZhY3Rfb3ZlcmFsbCwgcG9zdF9jaGFuZ2Vfb3ZlcmFsbCkgJT4lCiAgICBtdXRhdGUoY2FzZXNfYXZlcnRlZCA9IC5lcHJlZF9jb3VudGVyZi0uZXByZWQsCiAgICAgICAgICAgcGN0X2NoYW5nZSA9ICguZXByZWQgLSAuZXByZWRfY291bnRlcmYpLy5lcHJlZF9jb3VudGVyZikgJT4lCiAgICBncm91cF9ieSh7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgIG1lYW5fcWkoY2FzZXNfYXZlcnRlZCwgcGN0X2NoYW5nZSkgJT4lCiAgICB1bmdyb3VwKCkKCmxzdChjb3VudGVyX3Bvc3QsIGNvdW50ZXJfcG9zdF9vdmVyYWxsKQoKfQoKCmBgYAoKRnVuY3Rpb24gZm9yIHRpZHlpbmcgdXAgY291bnRlcmZhY3R1YWxzIChtb3N0bHkgZm9yIG1ha2luZyBuaWNlIHRhYmxlcykKCmBgYHtyfQoKdGlkeV9jb3VudGVyZmFjdHVhbHMgPC0gZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUoeWVhciA9IGFzLmNoYXJhY3Rlcih5ZWFyKSwKICAgICAgICAgICAgY2FzZXNfYXZlcnRlZCA9IGdsdWU6OmdsdWUoIntjYXNlc19hdmVydGVkfSAoe2Nhc2VzX2F2ZXJ0ZWQubG93ZXJ9IHRvIHtjYXNlc19hdmVydGVkLnVwcGVyfSkiKSwKICAgICAgICAgICAgcGN0X2NoYW5nZSA9IGdsdWU6OmdsdWUoIntwY3RfY2hhbmdlfSAoe3BjdF9jaGFuZ2UubG93ZXJ9IHRvIHtwY3RfY2hhbmdlLnVwcGVyfSkiKSwKICAgICAgICAgICAgZGlmZl9pbmMgPSBnbHVlOjpnbHVlKCJ7ZGlmZl9pbmMxMDBrfSAoe2RpZmZfaW5jMTAway5sb3dlcn0gdG8ge2RpZmZfaW5jMTAway51cHBlcn0pIiksCiAgICAgICAgICAgIHJyX2luYyA9IGdsdWU6OmdsdWUoIntycl9pbmMxMDBrfSAoe3JyX2luYzEwMGsubG93ZXJ9IHRvIHtycl9pbmMxMDBrLnVwcGVyfSkiKSkKfQoKCnRpZHlfY291bnRlcmZhY3R1YWxzX292ZXJhbGwgPC0gZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZSh5ZWFyID0gYXMuY2hhcmFjdGVyKHllYXIpLAogICAgICAgICAgICBjYXNlc19hdmVydGVkID0gZ2x1ZTo6Z2x1ZSgie2Nhc2VzX2F2ZXJ0ZWR9ICh7Y2FzZXNfYXZlcnRlZC5sb3dlcn0gdG8ge2Nhc2VzX2F2ZXJ0ZWQudXBwZXJ9KSIpLAogICAgICAgICAgICBwY3RfY2hhbmdlID0gZ2x1ZTo6Z2x1ZSgie3BjdF9jaGFuZ2V9ICh7cGN0X2NoYW5nZS5sb3dlcn0gdG8ge3BjdF9jaGFuZ2UudXBwZXJ9KSIpKQp9CgpgYGAKCgoKIyMjIDIuIERhdGEKCkltcG9ydCBkYXRhc2V0cyBmb3IgYW5hbHlzaXMKCiMjIyMgMi4xIFNoYXBlZmlsZXMKCk1ha2UgYSBtYXAgb2YgR2xhc2dvdyB3YXJkcwoKYGBge3J9CgpnbGFzZ293X3dhcmRzXzE5NTEgPC0gc3RfcmVhZChoZXJlKCJtYXBwaW5nL2dsYXNnb3dfd2FyZHNfMTk1MS5nZW9qc29uIikpCgpgYGAKCmBgYHtyfQoKI3JlYWQgaW4gU2NvdGxhbmQgYm91bmRhcnkKc2NvdGxhbmQgPC0gc3RfcmVhZChoZXJlKCJtYXBwaW5nL1Njb3RsYW5kX2JvdW5kYXJ5L1Njb3RsYW5kIGJvdW5kYXJ5LnNocCIpKQoKI21ha2UgYSBib3VuZGluZyBib3ggZm9yIEdsYXNnb3cKYmJveCA8LSBzdF9iYm94KGdsYXNnb3dfd2FyZHNfMTk1MSkgfD4gc3RfYXNfc2ZjKCkKCiNwbG90IHNjb3RsYW5kIHdpdGggYSBib3VuZGluZyBib3ggYXJvdW5kIHRoZSBDaXR5IG9mIEdsYXNnb3cKc2NvdGxhbmRfd2l0aF9iYm94IDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBzY290bGFuZCwgZmlsbD0iYW50aXF1ZXdoaXRlIikgKwogIGdlb21fc2YoZGF0YSA9IGJib3gsIGNvbG91ciA9ICIjQzYwQzMwIiwgZmlsbD0iYW50aXF1ZXdoaXRlIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BLCBsaW5ld2lkdGggPSAwLjUpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRUFGN0ZBIiwgc2l6ZSA9IDAuMykpCgojcGxvdCB0aGUgd2FyZHMKI25vdGUgd2UgdGlkeSB1cCBzb21lIG5hbWVzIHRvIGZpdCBvbiBtYXAKZ2xhc2dvd193YXJkX21hcCA8LSBnbGFzZ293X3dhcmRzXzE5NTEgJT4lCiAgbXV0YXRlKHdhcmQgPSBjYXNlX3doZW4od2FyZD09IlNoZXR0bGVzdG9uIGFuZCBUb2xsY3Jvc3MiIH4gIlNoZXR0bGVzdG9uIGFuZFxuVG9sbGNyb3NzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB3YXJkPT0iUGFydGljayAoV2VzdCkiIH4gIlBhcnRpY2tcbihXZXN0KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2FyZD09IlBhcnRpY2sgKEVhc3QpIiB+ICJQYXJ0aWNrXG4oRWFzdCkiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdhcmQ9PSJOb3J0aCBLZWx2aW4iIH4gIk5vcnRoXG5LZWx2aW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdhcmQ9PSJLaW5uaW5nIFBhcmsiIH4gIktpbm5pbmdcblBhcmsiLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB3YXJkKSkgJT4lCiAgCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGw9ZGl2aXNpb24pKSArCiAgZ2VvbV9zZl9sYWJlbChhZXMobGFiZWwgPSB3YXJkKSwgc2l6ZT0zLCBmaWxsPU5BLCBsYWJlbC5zaXplID0gTkEsIGNvbG91cj0iYmxhY2siKSArCiAgI3NjYWxlX2NvbG91cl9pZGVudGl0eSgpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiLCBuYW1lPSJDaXR5IG9mIEdsYXNnb3cgRGl2aXNpb24iKSArCiAgdGhlbWVfZ3JleSgpICsKICBsYWJzKHg9IiIsCiAgICAgICB5PSIiLAogICAgICAgZmlsbD0iRGl2aXNpb24iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BLCBsaW5ld2lkdGggPSAwLjUpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhbnRpcXVld2hpdGUiLCBzaXplID0gMC4zKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyZXk3OCIpKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41LCB0aXRsZS50aGVtZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpKQoKI2FkZCB0aGUgbWFwIG9mIHNjb3RsYW5kIGFzIGFuIGluc2V0CmdsYXNnb3dfd2FyZF9tYXAgKyBpbnNldF9lbGVtZW50KHNjb3RsYW5kX3dpdGhfYmJveCwgMC43NSwgMCwgMSwgMC40KQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczEudGlmZiIpLCBoZWlnaHQ9MTAsIHdpZHRoID0gMTIpCgoKYGBgCgojIyMgMy4gRGVub21pbmF0b3JzCgpMb2FkIGluIHRoZSBkYXRhc2V0cyBmb3IgZGVub25vbWlhdG9ycywgYW5kIGNoZWNrIGZvciBjb25zaXN0ZW5jeS4KCmBgYHtyfQoKb3ZlcmFsbF9wb3BzIDwtIHJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gIm92ZXJhbGxfcG9wdWxhdGlvbiIpCgpvdmVyYWxsX3BvcHMgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKI3NoaWZ0IHllYXIgdG8gbWlkcG9pbnQKb3ZlcmFsbF9wb3BzIDwtIG92ZXJhbGxfcG9wcyAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkKCmBgYAoKTm90ZSwgd2UgaGF2ZSB0aHJlZSBwb3B1bGF0aW9uIGVzdGltYXRlczoKCjEuIFBvcHVsYXRpb24gd2l0aG91dCBpbnN0aXR1dGlvbmFsaXNlZCBwZW9wbGUgb3IgcGVvcGxlIGluIHNoaXBwaW5nCjIuIFBvcHVsYXRpb24gaW4gaW5zdGl0dXRpb25zCjMuIFBvcHVsYXRpb24gaW4gc2hpcHBpbmcKCihQb3B1bGF0aW9uIGluIHNoaXBwaW5nIGlzIGVzdGltYXRlZCBmcm9tIHRoZSAxOTUxIGNlbnN1cywgc28gaXMgdGhlIHNhbWUgZm9yIG1vc3QgeWVhcnMpCgojIyMjIDMuMSBPdmVyYWxsIHBvcHVsYXRpb24KCkZpcnN0LCBwbG90IHRoZSB0b3RhbCBwb3B1bGF0aW9uCgpgYGB7cn0KCm92ZXJhbGxfcG9wcyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PXRvdGFsX3BvcHVsYXRpb24sIHg9eWVhcjIpLCBhbHBoYT0wLjUsIGNvbG91ciA9ICJtZWRpdW1zZWFncmVlbiIsIGZpbGw9Im1lZGl1bXNlYWdyZWVuIikgKwogIGdlb21fcG9pbnQoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiksIGNvbG91ciA9ICJtZWRpdW1zZWFncmVlbiIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2xhc2dvdyBDb3Jwb3JhdGlvbjogdG90YWwgcG9wdWxhdGlvbiIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMiLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICBjYXB0aW9uID0gIk1pZC15ZWFyIGVzdGltYXRlc1xuTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkKCgpgYGAKCk5vdyB0aGUgcG9wdWxhdGlvbiBleGNsdWRpbmcgaW5zdGl0dXRpb25hbGlzZWQgYW5kIHNoaXBwaW5nIHBvcHVsYXRpb24KCmBgYHtyfQoKb3ZlcmFsbF9wb3BzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgeD15ZWFyMiksIGFscGhhPTAuNSwgY29sb3VyID0gInB1cnBsZSIsIGZpbGw9InB1cnBsZSIpICsKICBnZW9tX3BvaW50KGFlcyh5PXBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIHg9eWVhcjIpLCBjb2xvdXIgPSAicHVycGxlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBwb3B1bGF0aW9uIGV4Y2x1ZGluZyBpbnN0aXR1dGlvbmFsaXNlZCBhbmQgc2hpcHBpbmciLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIlBvcHVsYXRpb24iLAogICAgY2FwdGlvbiA9ICJNaWQteWVhciBlc3RpbWF0ZXNcbk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpCgoKYGBgCgojIyMjIDMuMiBQb3B1bGF0aW9uIGJ5IFdhcmQKClRoZXJlIGFyZSA1IERpdmlzaW9ucyBjb250YWluaW5nIDM3IFdhcmRzIGluIHRoZSBHbGFzZ293IENvcnBvcmF0aW9uLCB3aXRoIGNvbnNpc3RlbnQgYm91bmRhcmllcyBvdmVyIHRpbWUuCgpgYGB7cn0KI2xvb2stdXAgdGFibGUgZm9yIGRpdmlzaW9ucyBhbmQgd2FyZHMKd2FyZF9sb29rdXAgPC0gcmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4Iiwgc2hlZXQgPSAiZGl2aXNpb25zX3dhcmRzIikKCgp3YXJkX3BvcHMgPC0gcmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4Iiwgc2hlZXQgPSAid2FyZF9wb3B1bGF0aW9uIikKCndhcmRfcG9wcyAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgojc2hpZnQgeWVhciB0byBtaWRwb2ludAp3YXJkX3BvcHMgPC0gd2FyZF9wb3BzICU+JQogIG11dGF0ZSh5ZWFyMiA9IHllYXIrMC41KQoKI0dldCB0aGUgRGl2aXNpb24gcG9wdWxhdGlvbgpkaXZpc2lvbl9wb3BzIDwtIHdhcmRfcG9wcyAlPiUKICBncm91cF9ieShkaXZpc2lvbiwgeWVhcikgJT4lCiAgc3VtbWFyaXNlKHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAgPSBzdW0ocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgaW5zdGl0dXRpb25zID0gc3VtKGluc3RpdHV0aW9ucywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgc2hpcHBpbmcgPSBzdW0oc2hpcHBpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHRvdGFsX3BvcHVsYXRpb24gPSBzdW0odG90YWxfcG9wdWxhdGlvbiwgbmEucm0gPSBUUlVFKSkKCmRpdmlzaW9uX3BvcHMgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpQbG90IHRoZSBvdmVyYWxsIHBvcHVsYXRpb24gYnkgRGl2aXNpb24gYW5kIFdhcmQKCmBgYHtyfQoKZGl2aXNpb25fcG9wcyAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT10b3RhbF9wb3B1bGF0aW9uLCB4PXllYXIyLCBjb2xvdXI9ZGl2aXNpb24sIGZpbGw9ZGl2aXNpb24pLCBhbHBoYT0wLjgpICsKICBnZW9tX3BvaW50KGFlcyh5PXRvdGFsX3BvcHVsYXRpb24sIHg9eWVhcjIsIGNvbG91cj1kaXZpc2lvbikpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAoZGl2aXNpb25+LikgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIsIG5hbWUgPSAiIikgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIiwgbmFtZSA9ICIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IHRvdGFsIHBvcHVsYXRpb24gYnkgRGl2aXNpb24iLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIlBvcHVsYXRpb24iLAogICAgY2FwdGlvbiA9ICJNaWQteWVhciBlc3RpbWF0ZXNcbk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgpgYGAKCmBgYHtyfQoKd2FyZF9wb3BzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiwgY29sb3VyPWRpdmlzaW9uLCBmaWxsPWRpdmlzaW9uKSkgKwogIGdlb21fcG9pbnQoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiwgY29sb3VyPWRpdmlzaW9uKSwgY29sb3VyPSJibGFjayIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAod2FyZH4uLCBuY29sPTYpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiLCBuYW1lPSJEaXZpc2lvbiIpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIsIG5hbWUgPSAiRGl2aXNpb24iKSArCiAgbGFicygKICAgIHggPSAiIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICBjYXB0aW9uID0gIk1pZC15ZWFyIGVzdGltYXRlc1xuTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczIudGlmZiIpLCBoZWlnaHQ9MTQsIHdpZHRoPTEyKQoKYGBgCgpBcHByb3hpbWF0ZWx5LCBob3cgbWFueSBwZXJzb24teWVhcnMgb2YgZm9sbG93LXVwIGRvIHdlIGhhdmU/CgpgYGB7cn0KCm92ZXJhbGxfcG9wcyAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3VtbWFyaXNlKGFjcm9zcyh5ZWFyLCBsZW5ndGgsIC5uYW1lcyA9ICJ5ZWFycyIpLAogICAgICAgICAgICBhY3Jvc3MoYyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCB0b3RhbF9wb3B1bGF0aW9uKSwgc3VtKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5kb3VibGUpLCBjb21tYSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpDaGFuZ2UgaW4gcG9wdWxhdGlvbiBieSB3YXJkCgpgYGB7cn0KCndhcmRfcG9wcyAlPiUKICBncm91cF9ieSh3YXJkKSAlPiUKICBzdW1tYXJpc2UocGN0X2NoYW5nZV9wb3AgPSAobGFzdChwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSAtIGZpcnN0KHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApKS9maXJzdChwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSkgJT4lCiAgbXV0YXRlKHBjdF9jaGFuZ2VfcG9wID0gcGVyY2VudChwY3RfY2hhbmdlX3BvcCkpICU+JQogIGFycmFuZ2UocGN0X2NoYW5nZV9wb3ApICU+JQogIGRhdGF0YWJsZSgpCiAgCgoKYGBgCgoKIyMjIyAzLjMgUG9wdWxhdGlvbiBieSBhZ2UgYW5kIHNleAoKYGBge3J9CgphZ2Vfc2V4IDwtIHJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gImFnZV9zZXhfcG9wdWxhdGlvbiIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhtYWxlLCBmZW1hbGUpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJzZXgiKQoKI2NvbGxhcHNlIGRvd24gdG8gc21hbGxlciBhZ2UgZ3JvdXBzIHRvIGJlIG1hbmFnZWFibGUKYWdlX3NleCA8LSBhZ2Vfc2V4ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZSA9PSAiMCB0byA0IiB+ICIwMCB0byAwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2UgPT0gIjUgdG8gOSIgfiAiMDUgdG8gMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIxMCB0byAxNCIgfiAiMDUgdG8gMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIxNSB0byAxOSIgfiAiMTUgdG8gMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIyMCB0byAyNCIgfiAiMTUgdG8gMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIyNSB0byAyOSIgfiAiMjUgdG8gMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIzMCB0byAzNCIgfiAiMjUgdG8gMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIzNSB0byAzOSIgfiAiMzUgdG8gNDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI0MCB0byA0NCIgfiAiMzUgdG8gNDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI0NSB0byA0OSIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI1MCB0byA1NCIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI1NSB0byA1OSIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICI2MCAmIHVwIikpICU+JQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUKICBtdXRhdGUodmFsdWUgPSBzdW0odmFsdWUpKSAlPiUKICB1bmdyb3VwKCkKCgoKbV9hZ2Vfc2V4IDwtIGxtKHZhbHVlIH4gc3BsaW5lczo6bnMoeWVhciwga25vdHMgPSAzKSphZ2Uqc2V4LCBkYXRhID0gYWdlX3NleCkKCnN1bW1hcnkobV9hZ2Vfc2V4KQoKYWdlX2xldmVscyA8LSBhZ2Vfc2V4ICU+JSBzZWxlY3QoYWdlKSAlPiUgZGlzdGluY3QoKSAlPiUgcHVsbCgpIAoKYWdlX3NleF9uZCA8LSAKICBjcm9zc2luZygKICAgIGFnZT1hZ2VfbGV2ZWxzLAogICAgc2V4PWMoIm1hbGUiLCAiZmVtYWxlIiksCiAgICB5ZWFyID0gMTk1MDoxOTYzCiAgKQoKcHJlZF9wb3BzIDwtIGFnZV9zZXhfbmQgJT4lIG1vZGVscjo6YWRkX3ByZWRpY3Rpb25zKG1fYWdlX3NleCkKCnByZWRfcG9wcyAlPiUKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1wcmVkLCBjb2xvdXI9YWdlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQoc2V4fi4pICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEsIGxpbWl0cyA9IGMoMCwgMTI1MDAwKSkKCiNIb3cgd2VsbCBkbyB0aGV5IG1hdGNoIHVwIHdpdGggb3VyIG92ZXJhbGwgcG9wdWxhdGlvbnM/CnByZWRfcG9wcyAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2Uoc3VtX3ByZWRfcG9wID0gc3VtKHByZWQpKSAlPiUKICByaWdodF9qb2luKG92ZXJhbGxfcG9wcykgJT4lCiAgc2VsZWN0KHllYXIsIHN1bV9wcmVkX3BvcCwgcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbikgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHN1bV9wcmVkX3BvcCwgcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbikpICU+JQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXZhbHVlLCBjb2xvdXI9bmFtZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSwgbGltaXRzID0gYyg4MDAwMDAsIDEyNTAwMDApKQoKcHJlZF9wb3BzICU+JQogIGdyb3VwX2J5KHllYXIsIHNleCkgJT4lCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShwcmVkKSkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgbXV0YXRlKHNleF9yYXRpbyA9IGZpcnN0KHN1bSkvbGFzdChzdW0pKQpgYGAKCgoKCgpQb3B1bGF0aW9uIHB5cmFtaWRzCgpgYGB7cn0KCmxhYmVsX2FicyA8LSBmdW5jdGlvbih4KSB7CiAgY29tbWEoYWJzKHgpKQp9CgoKcHJlZF9wb3BzICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBtdXRhdGUoeWVhcl9wb3AgPSBzdW0ocHJlZCksCiAgICAgICAgIGFnZV9zZXhfcGN0ID0gcGVyY2VudChwcmVkL3llYXJfcG9wLCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Im1hbGUiIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iZmVtYWxlIiB+ICJGZW1hbGUiKSkgJT4lCiAgZ2dwbG90KAogICAgYWVzKHggPSBhZ2UsIGZpbGwgPSBzZXgsIAogICAgICAgIHkgPSBpZmVsc2UodGVzdCA9IHNleCA9PSAiRmVtYWxlIix5ZXMgPSAtcHJlZCwgbm8gPSBwcmVkKSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gYWdlX3NleF9wY3QpLAogICAgICAgICAgICBwb3NpdGlvbj0gcG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSwgY29sb3VyPSJibGFjayIsIHNpemU9Mi41KSArCiAgZmFjZXRfd3JhcCh5ZWFyfi4sIG5jb2w9NykgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX2FicykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdCA9IDEsIHZqdXN0PTAuNSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iIiwgeT0iIikgCgoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczMudGlmZiIpLCB3aWR0aD0xMCkKCgpgYGAKCk5vdCBwZXJmZWN0LCBidXQgcmVzb25hYmx5IGdvb2QuIEJ1dCBhaGhoaGguLi4gdGhlIGFnZSBncm91cHMgZG9uJ3QgYWxpZ24gd2l0aCB0aGUgY2FzZSBub3RpZmljYXRpb24gYWdlIGdyb3VwcyEgQ29tZSBiYWNrIHRvIHRoaW5rIGFib3V0IHRoaXMgbGF0ZXIuCgoKIyMjIDQuIFR1YmVyY3Vsb3NpcyBjYXNlcwoKSW1wb3J0IHRoZSB0dWJlcmN1bG9zaXMgY2FzZXMgZGF0YXNldAoKCiMjIyMgNC4xIE92ZXJhbGwgbm90aWZpY2F0aW9ucwoKT3ZlcmFsbCwgYnkgeWVhci4KCmBgYHtyfQoKY2FzZXNfYnlfeWVhciA8LSByZWFkX3hsc3goIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gImJ5X3llYXIiKQoKY2FzZXNfYnlfeWVhciU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApjYXNlc19ieV95ZWFyIDwtIGNhc2VzX2J5X3llYXIgJT4lCiAgbXV0YXRlKHllYXIyID0geWVhciswLjUpCgpgYGAKClBsb3QgdGhlIG92ZXJhbGwgbnVtYmVyIG9mIGNhc2Ugbm90aWZpZWQgcGVyIHllYXIsIGJ5IHB1bG1vbmFyeSBhbmQgZXh0cmEgcHVsbW9uYXJ5IGNsYXNzaWZpY2F0aW9uLgoKYGBge3J9CgpjYXNlc19ieV95ZWFyICU+JQogIHNlbGVjdCgtdG90YWxfbm90aWZpY2F0aW9ucywgLXllYXIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhwdWxtb25hcnlfbm90aWZpY2F0aW9ucywgYG5vbi1wdWxtb25hcnlfbm90aWZpY2F0aW9uc2ApKSAlPiUKICBtdXRhdGUobmFtZSA9IGNhc2Vfd2hlbihuYW1lID09ICJwdWxtb25hcnlfbm90aWZpY2F0aW9ucyIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICJub24tcHVsbW9uYXJ5X25vdGlmaWNhdGlvbnMiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PXZhbHVlLCB4PXllYXIyLCBncm91cCA9IG5hbWUsIGZpbGw9bmFtZSksIGFscGhhPTAuNSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIsIG5hbWU9IiIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2xhc2dvdyBDb3Jwb3JhdGlvbjogVHViZXJjdWxvc2lzIG5vdGlmaWNhdGlvbnMiLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzLCBieSBUQiBkaXNlYXNlIGNsYXNzaWZpY2F0aW9uIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIk51bWJlciBvZiBjYXNlcyIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICAKCmBgYAoKIyMjIyA0LjIgTm90aWZpY2F0aW9ucyBieSBEaXZpc2lvbgoKUmVhZCBpbiB0aGUgZGF0YXNldHMgYW5kIG1lcmdlIHRvZ2V0aGVyLgoKYGBge3J9CgojbGlzdCBhbGwgdGhlIHNoZWV0cwphbGxfc2hlZXRzIDwtIGV4Y2VsX3NoZWV0cygiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4IikKCiNnZXQgdGhlIHdhcmQgc2hlZXRzCndhcmRfc2hlZXRzIDwtIGVuZnJhbWUoYWxsX3NoZWV0cykgJT4lCiAgZmlsdGVyKGdyZXBsKCJieV93YXJkIiwgdmFsdWUpKSAlPiUKICBwdWxsKHZhbHVlKQoKCmNhc2VzX2J5X3dhcmRfc2V4X3llYXIgPC0gbWFwX2RmKHdhcmRfc2hlZXRzLCB+cmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9IC4pKQoKY2FzZXNfYnlfd2FyZF9zZXhfeWVhciAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKCkFnZ3JlZ2F0ZSB0b2dldGhlciB0byBnZXQgY2FzZXMgYnkgZGl2aXNpb24KCmBgYHtyfQoKY2FzZXNfYnlfZGl2aXNpb24gPC0gY2FzZXNfYnlfd2FyZF9zZXhfeWVhciAlPiUKICBsZWZ0X2pvaW4od2FyZF9sb29rdXApICU+JQogIGdyb3VwX2J5KGRpdmlzaW9uLCB5ZWFyLCB0Yl90eXBlKSAlPiUKICBzdW1tYXJpc2UoY2FzZXMgPSBzdW0oY2FzZXMsIG5hLnJtID0gVFJVRSkpCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApjYXNlc19ieV9kaXZpc2lvbiA8LSBjYXNlc19ieV9kaXZpc2lvbiAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgdW5ncm91cCgpCgpjYXNlc19ieV9kaXZpc2lvbiAgJT4lCiAgc2VsZWN0KC15ZWFyMikgJT4lCiAgc2VsZWN0KHllYXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKCmNhc2VzX2J5X2RpdmlzaW9uICU+JQogIG11dGF0ZSh0Yl90eXBlID0gY2FzZV93aGVuKHRiX3R5cGUgPT0gIlB1bG1vbmFyeSIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB0Yl90eXBlID09ICJOb24tUHVsbW9uYXJ5IiB+ICJFeHRyYS1wdWxtb25hcnkgVEIiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT1jYXNlcywgeD15ZWFyMiwgZ3JvdXAgPSB0Yl90eXBlLCBmaWxsPXRiX3R5cGUpLCBhbHBoYT0wLjUpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIGZhY2V0X3dyYXAoZGl2aXNpb25+Liwgc2NhbGVzID0gImZyZWVfeSIpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBub3RpZmljYXRpb25zIGJ5IERpdmlzaW9uIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJOdW1iZXIgb2YgY2FzZXMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpgYGAKCiMjIyMgNC4zIE5vdGlmaWNhdGlvbnMgYnkgd2FyZAoKYGBge3J9CgoKY2FzZXNfYnlfd2FyZCA8LSBjYXNlc19ieV93YXJkX3NleF95ZWFyICU+JQogIGdyb3VwX2J5KHdhcmQsIHllYXIsIHRiX3R5cGUpICU+JQogIHN1bW1hcmlzZShjYXNlcyA9IHN1bShjYXNlcywgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCgpjYXNlc19ieV93YXJkICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgc2VsZWN0KHllYXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZGF0YXRhYmxlKCkKCiNzaGlmdCB5ZWFyIHRvIG1pZHBvaW50CmNhc2VzX2J5X3dhcmQgPC0gY2FzZXNfYnlfd2FyZCAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkKCmNhc2VzX2J5X3dhcmQgJT4lCiAgbXV0YXRlKHRiX3R5cGUgPSBjYXNlX3doZW4odGJfdHlwZSA9PSAiUHVsbW9uYXJ5IiB+ICJQdWxtb25hcnkgVEIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRiX3R5cGUgPT0gIk5vbi1QdWxtb25hcnkiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWNhc2VzLCB4PXllYXIyLCBncm91cCA9IHRiX3R5cGUsIGZpbGw9dGJfdHlwZSksIGFscGhhPTAuOCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgZmFjZXRfd3JhcCh3YXJkfi4sIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgbm90aWZpY2F0aW9ucyBieSBXYXJkIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJOdW1iZXIgb2YgY2FzZXMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgpgYGAKCiMjIyMgNC40IE5vdGlmaWNhdGlvbnMgYnkgYWdlIGFuZCBzZXgKCkFzIHdlIGRvbid0IGhhdmUgZGVub21pbmF0b3JzLCB3ZSB3aWxsIGp1c3QgbW9kZWwgdGhlIGNoYW5nZSBpbiBjb3VudHMuCgpgYGB7cn0KCiNsaXN0IGFsbCB0aGUgc2hlZXRzCmFsbF9zaGVldHMgPC0gZXhjZWxfc2hlZXRzKCIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giKQoKI2dldCB0aGUgd2FyZCBzaGVldHMKYWdlX3NleF9zaGVldHMgPC0gZW5mcmFtZShhbGxfc2hlZXRzKSAlPiUKICBmaWx0ZXIoZ3JlcGwoImJ5X2FnZV9zZXgiLCB2YWx1ZSkpICU+JQogIHB1bGwodmFsdWUpCgoKY2FzZXNfYnlfYWdlX3NleCA8LSBtYXBfZGYoYWdlX3NleF9zaGVldHMsIH5yZWFkX3hsc3gocGF0aCA9ICIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gLikpCgpjYXNlc19ieV9hZ2Vfc2V4ICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKCiMjIyA0LjUgVXB0YWtlIG9mIHNjcmVlbmluZwoKV2hhdCBwZXJjZW50YWdlIG9mIGFkdWx0cyAoMTUrIHBhcnRpY2lwYXRlZCBpbiB0aGUgaW50ZXJ2ZW50aW9uIGluIDE5NTcpPwoKTm90ZSB0aGF0IGluIHRoZSBSZXBvcnQgb2YgU2lyIEtlbm5ldGggQ293YW4sIHdlIGhhdmUgdGhlIGZvbGxvd2luZyBlc3RpbWF0ZXMgb2YgcGFydGljaXBhdGlvbiAod2Ugd2lsbCB1c2UgdGhlc2UgZm9yIHRoZSBtYW51c2NyaXB0LCBhcyB0aGV5IGFyZSBub3QgYmFzZWQgb24gbXkgZXN0aW1hdGVzKQoKYGBge3J9Cm1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbiA8LSAyODE4NzUKZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24gPC0gMzQwNDc0Cm1hbGVfYWR1bHRfcmVzaWRlbnRfcG9wdWxhdGlvbiA8LSAzODE3MTMKZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24gPC0gNDM3NTg4Cgojb3ZlcmFsbCBwYXJ0aWNpcGF0aW9uCihtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24rZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24pLyhtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24rZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24pCgojbWFsZSBwYXJ0aWNpcGF0aW9uCm1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbi9tYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24KCiNmZW1hbGUgcGFydGljaXBhdGlvbgpmZW1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbi9mZW1hbGVfYWR1bHRfcmVzaWRlbnRfcG9wdWxhdGlvbgoKCmBgYAoKCkxvb2sgYXQgdXB0YWtlIG9mIHNjcmVlbmluZyBieSBhZ2UgYW5kIHNleAoKYGBge3J9CgoKdXB0YWtlX2FnZV9zZXggPC0gcmVhZF94bHN4KCIyMDI0LTAzLTI2X21hc3NfeHJheV91cHRha2UueGxzeCIsIHNoZWV0ID0gInVwdGFrZV9hZ2Vfc2V4IikKCnVwdGFrZV9ncmFwaCA8LSB1cHRha2VfYWdlX3NleCAlPiUKICBtdXRhdGUodXB0YWtlID0gZXhhbWluZWQvcmVzaWRlbnRfcG9wdWxhdGlvbikgJT4lCiAgbXV0YXRlKGV4YW1pbmVkX2wgPSBjb21tYShleGFtaW5lZCksCiAgICAgICAgIHJlc2lkZW50X3BvcHVsYXRpb25fbCA9IGNvbW1hKHJlc2lkZW50X3BvcHVsYXRpb24pLAogICAgICAgICB1cHRha2VfbCA9IHBlcmNlbnQodXB0YWtlLCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUobGFiZWwgPSBnbHVlKCJ7ZXhhbWluZWRfbH0ve3Jlc2lkZW50X3BvcHVsYXRpb25fbH0gKHt1cHRha2VfbH0pIikpICU+JQogIGZpbHRlcihhZ2UgIT0iMDAtMTQiKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Im0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iZiIgfiAiRmVtYWxlIikpICU+JQogIGdncGxvdChhZXMoeD1hZ2UsIHk9dXB0YWtlLCBncm91cD1zZXgsIGZpbGw9c2V4KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXVwdGFrZV9sKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjg1KSx2anVzdD0yKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1wZXJjZW50KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0NEN0FDNSIsICJjYWRldGJsdWUzIiksIG5hbWU9IiIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iIiwgeT0iIikKCgoKCmBgYApDb21iaW5lIGZpZ3VyZSB3aXRoIHRhYmxlIGZvciBzaW5nbGUgZmlndXJlLgoKYGBge3J9Cgp1cHRha2VfdGFibGUgPC0gdXB0YWtlX2FnZV9zZXggJT4lCiAgbXV0YXRlKHJlc2lkZW50X3BvcHVsYXRpb24gPSBjb21tYShyZXNpZGVudF9wb3B1bGF0aW9uKSwKICAgICAgICAgZXhhbWluZWQgPSBjb21tYShleGFtaW5lZCkpICU+JQogIHJlbmFtZShTZXggPSBzZXgsCiAgICAgICAgIEFnZSA9IGFnZSwKICAgICAgICAgYFJlc2lkZW50IHBvcHVsYXRpb25gID0gcmVzaWRlbnRfcG9wdWxhdGlvbiwKICAgICAgICAgRXhhbWluZWQgPSBleGFtaW5lZCkgJT4lCiAgbXV0YXRlKFNleCA9IGNhc2Vfd2hlbihTZXg9PSJtIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIFNleD09ImYiIH4gIkZlbWFsZSIpKQoKdXB0YWtlX2dyYXBoIC8gZ3JpZEV4dHJhOjp0YWJsZUdyb2IodXB0YWtlX3RhYmxlLCByb3dzID0gTlVMTCkKCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3M0LnRpZmYiKSwgaGVpZ2h0PTEwKQoKYGBgCgoKClVwdGFrZSBieSBkaXZpc2lvbgoKYGBge3J9Cgp1cHRha2VfZGl2aXNpb24gPC0gcmVhZF94bHN4KCIyMDI0LTAzLTI2X21hc3NfeHJheV91cHRha2UueGxzeCIsIHNoZWV0ID0gInVwdGFrZV9kaXZpc2lvbiIpCgpkaXZpc2lvbl9wb3BzICU+JQogIGZpbHRlcih5ZWFyPT0xOTU3KSAlPiUKICBzZWxlY3QoZGl2aXNpb24sIHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApICU+JQogIGxlZnRfam9pbih1cHRha2VfZGl2aXNpb24pICU+JQogIG11dGF0ZShwY3RfcG9wX2V4YW1pbmVkID0gZXhhbWluZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCkKCgpgYGAKCgoKCiMjIyA1IFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzCgojIyMjIDUuMSBPdmVyYWxsIFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzCgpOb3cgY2FsY3VsYXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzIHBlciAxMDAsMDAwIHBvcHVsYXRpb24KCk1lcmdlIHRoZSBub3RpZmljYXRpb24gYW5kIHBvcHVsYXRpb24gZGVub21pbmF0b3IgZGF0YXNldHMgdG9nZXRoZXIuCgpIZXJlIHdlIG5lZWQgdG8gaW5jbHVkZSB0aGUgd2hvbGUgcG9wdWxhdGlvbiAod2l0aCBzaGlwcGluZyBhbmQgaW5zdGl0dXRpb25zKSBhcyB0aGV5IGFyZSBpbmNsdWRlZCBpbiB0aGUgbm90aWZpY2F0aW9ucy4KCmBgYHtyfQoKb3ZlcmFsbF9pbmMgPC0gb3ZlcmFsbF9wb3BzICU+JQogIGxlZnRfam9pbihjYXNlc19ieV95ZWFyKQoKb3ZlcmFsbF9pbmMgPC0gb3ZlcmFsbF9pbmMgJT4lCiAgbXV0YXRlKGluY19wdWxtXzEwMGsgPSBwdWxtb25hcnlfbm90aWZpY2F0aW9ucy90b3RhbF9wb3B1bGF0aW9uKjEwMDAwMCwKICAgICAgICAgaW5jX2VwXzEwMGsgPSBgbm9uLXB1bG1vbmFyeV9ub3RpZmljYXRpb25zYC90b3RhbF9wb3B1bGF0aW9uKjEwMDAwMCwKICAgICAgICAgaW5jXzEwMGsgPSB0b3RhbF9ub3RpZmljYXRpb25zL3RvdGFsX3BvcHVsYXRpb24qMTAwMDAwKQoKb3ZlcmFsbF9pbmMgJT4lCiAgc2VsZWN0KHllYXIsIGluY18xMDBrLCBpbmNfcHVsbV8xMDBrLCBpbmNfZXBfMTAwaykgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhpbmNfMTAwaywgaW5jX3B1bG1fMTAwaywgaW5jX2VwXzEwMGspLAogICAgICAgICAgICAuZnVucyA9IGZ1bnMocm91bmQpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpgYGB7cn0KCm92ZXJhbGxfaW5jICU+JQogIHNlbGVjdCh5ZWFyMiwgaW5jX3B1bG1fMTAwaywgaW5jX2VwXzEwMGspICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhpbmNfcHVsbV8xMDBrLCBgaW5jX2VwXzEwMGtgKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSBjYXNlX3doZW4obmFtZSA9PSAiaW5jX3B1bG1fMTAwayIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICJpbmNfZXBfMTAwayIgfiAiRXh0cmEtcHVsbW9uYXJ5IFRCIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9dmFsdWUsIHg9eWVhcjIsIGdyb3VwID0gbmFtZSwgZmlsbD1uYW1lKSwgYWxwaGE9MC41KSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgY2FzZSBub3RpZmljYXRpb24gcmF0ZSIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMsIGJ5IFRCIGRpc2Vhc2UgY2xhc3NpZmljYXRpb24iLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgpgYGAKCkNoYW5nZSBpbiBjYXNlIG5vdGlmaWNhdGlvbiByYXRlcyBwcmUtaW50ZXJ2ZW50aW9uCgpgYGB7cn0KI3ByZS1BQ0YKb3ZlcmFsbF9pbmMgJT4lCiAgZmlsdGVyKHllYXIgJWluJSAxOTUwOjE5NTYpICU+JQogIHN1bW1hcmlzZShjaGFuZ2UgPSAoKChsYXN0KGluY19wdWxtXzEwMGspLWZpcnN0KGluY19wdWxtXzEwMGspKS9maXJzdChpbmNfcHVsbV8xMDBrKSkvNykqMTAwKQoKI3Bvc3QtQUNGCm92ZXJhbGxfaW5jICU+JQogIGZpbHRlcih5ZWFyICVpbiUgMTk1ODoxOTYzKSAlPiUKICBzdW1tYXJpc2UoY2hhbmdlID0gKCgobGFzdChpbmNfcHVsbV8xMDBrKS1maXJzdChpbmNfcHVsbV8xMDBrKSkvZmlyc3QoaW5jX3B1bG1fMTAwaykpLzYpKjEwMCkKCmBgYAoKCgoKIyMjIyA1LjIgVEIgY2FzZSBub3RpZmljYXRpb24gcmF0ZXMgYnkgRGl2aXNpb24KCmBgYHtyfQoKZGl2aXNpb25faW5jIDwtIGRpdmlzaW9uX3BvcHMgJT4lCiAgbGVmdF9qb2luKGNhc2VzX2J5X2RpdmlzaW9uKQoKCmRpdmlzaW9uX2luYyA8LSBkaXZpc2lvbl9pbmMgJT4lCiAgbXV0YXRlKGluY18xMDBrID0gY2FzZXMvdG90YWxfcG9wdWxhdGlvbioxMDAwMDApCgpkaXZpc2lvbl9pbmMgJT4lCiAgc2VsZWN0KHllYXIsIGRpdmlzaW9uLCB0Yl90eXBlLCBpbmNfMTAwaykgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhpbmNfMTAwayksCiAgICAgICAgICAgIC5mdW5zID0gZnVucyhyb3VuZCkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpgYGB7cn0KCmRpdmlzaW9uX2luYyAlPiUKICBtdXRhdGUodGJfdHlwZSA9IGNhc2Vfd2hlbih0Yl90eXBlID09ICJQdWxtb25hcnkiIH4gIlB1bG1vbmFyeSBUQiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGJfdHlwZSA9PSAiTm9uLVB1bG1vbmFyeSIgfiAiRXh0cmEtcHVsbW9uYXJ5IFRCIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9aW5jXzEwMGssIHg9eWVhcjIsIGdyb3VwID0gdGJfdHlwZSwgZmlsbD10Yl90eXBlKSwgYWxwaGE9MC41KSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpICsKICBmYWNldF93cmFwKGRpdmlzaW9ufi4pICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBjYXNlIG5vdGlmaWNhdGlvbiByYXRlLCBieSBEaXZpc2lvbiIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMsIGJ5IFRCIGRpc2Vhc2UgY2xhc3NpZmljYXRpb24iLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1Nylcbk5vdGU6IGV4dHJhLXB1bG1vbmFyeSBUQiBjYXNlcyBieSBEaXZpc2lvbi9XYXJkIG5vdCByZXBvcnRlZCBpbiAxOTYyLTE5NjMiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgpgYGAKCiMjIyMgNS4yIFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzIGJ5IFdhcmQKCkhlcmUgd2Ugd2lsbCBmaWx0ZXIgb3V0IHRoZSBpbnN0aXR1dGlvbnMgYW5kIGhhcmJvdXIgZnJvbSB0aGUgZGVub21pbmF0b3JzLCBhcyB3ZSBkb24ndCBoYXZlIHJlbGlhYmxlIHBvcHVsYXRpb24gZGVub21pbmF0b3JzIGZvciB0aGVtLgoKYGBge3J9Cgp3YXJkX2luYyA8LSB3YXJkX3BvcHMgJT4lCiAgbGVmdF9qb2luKGNhc2VzX2J5X3dhcmQpCgoKd2FyZF9pbmMgPC0gd2FyZF9pbmMgJT4lCiAgbXV0YXRlKGluY18xMDBrID0gY2FzZXMvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApCgp3YXJkX2luYyAlPiUKICBzZWxlY3QoeWVhciwgd2FyZCwgdGJfdHlwZSwgaW5jXzEwMGspICU+JQogIG11dGF0ZV9hdCgudmFycyA9IHZhcnMoaW5jXzEwMGspLAogICAgICAgICAgICAuZnVucyA9IGZ1bnMocm91bmQpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKCmBgYHtyfQoKd2FyZF9pbmMgJT4lCiAgbXV0YXRlKHRiX3R5cGUgPSBjYXNlX3doZW4odGJfdHlwZSA9PSAiUHVsbW9uYXJ5IiB+ICJQdWxtb25hcnkgVEIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRiX3R5cGUgPT0gIk5vbi1QdWxtb25hcnkiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWluY18xMDBrLCB4PXllYXIyLCBncm91cCA9IHRiX3R5cGUsIGZpbGw9dGJfdHlwZSksIGFscGhhPTAuNSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgZmFjZXRfd3JhcCh3YXJkfi4pICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBjYXNlIG5vdGlmaWNhdGlvbiByYXRlLCBieSBXYXJkIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJJbmNpZGVuY2UgKHBlciAxMDAsMDAwKSIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpXG5Ob3RlOiBleHRyYS1wdWxtb25hcnkgVEIgY2FzZXMgYnkgRGl2aXNpb24vV2FyZCBub3QgcmVwb3J0ZWQgaW4gMTk2Mi0xOTYzIgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgoKYGBgCgpPbiBhIG1hcAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CgpzdF9hc19zZihsZWZ0X2pvaW4od2FyZF9pbmMsIGdsYXNnb3dfd2FyZHNfMTk1MSkpICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGw9aW5jXzEwMGspKSArCiAgZmFjZXRfd3JhcCh5ZWFyfi4sIG5jb2wgPSA3KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0iQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgICAgICAgICAgICAgICAgICAgICBvcHRpb24gPSAiQSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMiwgImNtIiksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2NvbG9yYmFyKHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKQoKCgpgYGAKCgojIyMgNi4gVEIgTW9ydGFsaXR5CgojIyMjIDYuMSBPdmVyYWxsIE1vcnRhbGl0eQoKSW1wb3J0IHRoZSBUQiBtb3J0YWxpdHkgZGF0YS4KCkZpcnN0LCBvdmVyYWxsIGRlYXRocy4gTm90ZSB0aGF0IGluIHRoZSBvcmlnaW5hbCByZXBvcnRzLCB3ZSBoYXZlIGEgcHVsbW9uYXJ5IFRCIGRlYXRoIHJhdGUgcGVyIG1pbGxpb24gZm9yIGFsbCB5ZWFycywgYW5kIG51bWJlcnMgb2YgcHVsbW9uYXJ5IFRCIGRlYXRocyBmb3IgZWFjaCB5ZWFyIGFwYXJ0IGZyb20gMTk1MC4KCmBgYHtyfQoKI2dldCB0aGUgb3ZlcmFsbCBtb3J0YWxpdHkgc2hlZXRzCmRlYXRoc19zaGVldHMgPC0gZW5mcmFtZShhbGxfc2hlZXRzKSAlPiUKICBmaWx0ZXIoZ3JlcGwoImRlYXRocyIsIHZhbHVlKSkgJT4lCiAgcHVsbCh2YWx1ZSkKCgpvdmVyYWxsX2RlYXRocyA8LSBtYXBfZGYoZGVhdGhzX3NoZWV0cywgfnJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAuKSkKCm92ZXJhbGxfZGVhdGhzICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgoKYGBgCgpQbG90IHRoZSByYXcgbnVtYmVycyBvZiBwdWxtb25hcnkgZGVhdGhzCgpgYGB7cn0KCm92ZXJhbGxfZGVhdGhzICU+JQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXB1bG1vbmFyeV9kZWF0aHMpKSArCiAgZ2VvbV9saW5lKGNvbG91ciA9ICIjREUwRDkyIikgKwogIGdlb21fcG9pbnQoY29sb3VyID0gIiNERTBEOTIiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIGxhYnMoeT0iUHVsbW9uYXJ5IFRCIGRlYXRocyBwZXIgeWVhciIsCiAgICAgICB4ID0gIlllYXIiLAogICAgICAgdGl0bGUgPSAiTnVtYmVycyBvZiBwdWxtb25hcnkgVEIgZGVhdGhzIiwKICAgICAgIHN1YnRpdGxlID0gIkdsYXNnb3csIDE5NTAtMTk2MyIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpXG5Ob3RlOiBubyBkYXRhIGZvciAxOTUwIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQoKCmBgYAoKTm93IHRoZSBpbmNpZGVuY2Ugb2YgcHVsbW9uYXJ5IFRCIGRlYXRoCgpgYGB7cn0Kb3ZlcmFsbF9kZWF0aHMgJT4lCiAgZ2dwbG90KGFlcyh4PXllYXIsIHk9cHVsbW9uYXJ5X2RlYXRoX3JhdGVfcGVyXzEwMGspKSArCiAgZ2VvbV9saW5lKGNvbG91ciA9ICIjNEQ2Q0ZBIikgKwogIGdlb21fcG9pbnQoY29sb3VyID0gIiM0RDZDRkEiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzKSArCiAgbGFicyh5PSJBbm51YWwgaW5jaWRlbmNlIG9mIGRlYXRoIChwZXIgMTAwLDAwMCkiLAogICAgICAgeCA9ICJZZWFyIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9kZWF0aHMudGlmZiIpLCB3aWR0aD0xMCkKCmBgYAoKCiMjIyA2LiBUYWJsZSAxCgpNYWtlIFRhYmxlIDEgaGVyZSwgYW5kIHNhdmUgZm9yIHB1YmxpY2F0aW9uLgoKYGBge3J9CgpvdmVyYWxsX3BvcHMgJT4lIAogIHNlbGVjdCh5ZWFyLCB0b3RhbF9wb3B1bGF0aW9uKSAlPiUKICBsZWZ0X2pvaW4ob3ZlcmFsbF9pbmMgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIAogICAgICAgICAgICAgICAgICAgICBwdWxtb25hcnlfbm90aWZpY2F0aW9ucywgaW5jX3B1bG1fMTAwaywKICAgICAgICAgICAgICAgICAgICAgYG5vbi1wdWxtb25hcnlfbm90aWZpY2F0aW9uc2AsIGluY19lcF8xMDBrLAogICAgICAgICAgICAgICAgICAgICB0b3RhbF9ub3RpZmljYXRpb25zLCBpbmNfMTAwaykpICU+JQogIGxlZnRfam9pbihvdmVyYWxsX2RlYXRocyAlPiUKICAgICAgICAgICAgICBzZWxlY3QoeWVhciwKICAgICAgICAgICAgICAgICAgICAgcHVsbW9uYXJ5X2RlYXRocywgcHVsbW9uYXJ5X2RlYXRoX3JhdGVfcGVyXzEwMGspKSAlPiUKICBtdXRhdGUocGVyY2VudF9wdWxtb25hcnkgPSBwZXJjZW50KHB1bG1vbmFyeV9ub3RpZmljYXRpb25zLyh0b3RhbF9ub3RpZmljYXRpb25zICksIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfnJvdW5kKC4sIGRpZ2l0cz0xKSkpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkKCmBgYAoKQ29tcGFyaXNvbiBmbyBhZ2Utc2V4IGRpc3RyaWJ1dGlvbiBvZiBjYXNlcyBpbiAxOTUwLTE5NTYgdnMuIDE5NTcKCmBgYHtyfQoKbGFiZWxfYWJzMiA8LSBmdW5jdGlvbih4KSB7CiAgcGVyY2VudChhYnMoeCkpCn0KCgoKY2FzZXNfYnlfYWdlX3NleCAlPiUgCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oeWVhciAlaW4lIGMoMTk1MDoxOTU2KSB+ICJhLiBwcmUtYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU3KSB+ICJiLiBhY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTg6MTk2MykgfiAiYy4gcG9zdC1hY2YiKSkgJT4lCiAgZ3JvdXBfYnkoYWNmX3BlcmlvZCwgYWdlLCBzZXgpICU+JQogIHN1bW1hcmlzZShjYXNlcyA9IHN1bShjYXNlcykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShhY2ZfcGVyaW9kKSAlPiUKICBtdXRhdGUocGVyaW9kX3RvdGFsID0gc3VtKGNhc2VzKSkgJT4lCiAgbXV0YXRlKHBjdCA9IGNhc2VzL3BlcmlvZF90b3RhbCkgJT4lCiAgbXV0YXRlKHBjdDIgPSBjYXNlX3doZW4oc2V4PT0iRiIgfiAtcGN0LAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBwY3QpKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgICAgICAgICAgICAgICAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIlByZS1BQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJiLiBhY2YiIH4gIkFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImMuIHBvc3QtYWNmIiB+ICJQb3N0LUFDRiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD0wKSwgbGluZXR5cGU9MikgKwogIGdlb21fcG9pbnQoYWVzKHg9cGN0Mix5PWFnZSwgY29sb3VyPWZjdF9yZWxldmVsKGFjZl9wZXJpb2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHJlLUFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQb3N0LUFDRiIpKSwgc3RhdD0iaWRlbnRpdHkiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1sYWJlbF9hYnMyLCBsaW1pdHMgPSBjKC0wLjIsIDAuMikpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSkgKwogIHRoZW1lX2dyZXkoYmFzZV9mYW1pbHkgPSAiQXB0b3MiKSArCiAgbGFicyh4PSAiPC0gRmVtYWxlICAgICAgICAgICAgICAgIFBlcmNlbnQgb2YgY2FzZXMgICAgICAgICAgICAgIE1hbGUgLT4iLAogICAgICAgeT0iIikgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3M1LnRpZmYiKSkKCmBgYAoKCgpQcmVwYXJlIHRoZSBkYXRhc2V0cyBmb3IgbW9kZWxsaW5nCgpgYGB7cn0KCm1kYXRhIDwtIHdhcmRfaW5jICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oeWVhciAlaW4lIGMoMTk1MDoxOTU2KSB+ICJhLiBwcmUtYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU3KSB+ICJiLiBhY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTg6MTk2MykgfiAiYy4gcG9zdC1hY2YiKSkgJT4lCiAgZ3JvdXBfYnkod2FyZCkgJT4lCiAgbXV0YXRlKHlfbnVtID0gcm93X251bWJlcigpKSAlPiUKICB1bmdyb3VwKCkKCgptZGF0YV9leHRyYXB1bG1vbmFyeSA8LSB3YXJkX2luYyAlPiUKICBmaWx0ZXIodGJfdHlwZT09Ik5vbi1QdWxtb25hcnkiKSAlPiUKICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbih5ZWFyICVpbiUgYygxOTUwOjE5NTYpIH4gImEuIHByZS1hY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTcpIH4gImIuIGFjZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciAlaW4lIGMoMTk1ODoxOTYzKSB+ICJjLiBwb3N0LWFjZiIpKSAlPiUKICBncm91cF9ieSh3YXJkKSAlPiUKICBtdXRhdGUoeV9udW0gPSByb3dfbnVtYmVyKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZmlsdGVyKHllYXI8PTE5NjEpICNubyBkYXRhIGZvciAxOTYyIGFuZCAxOTYzCgoKI3NjYWZmb2xkIGZvciBvdmVyYWxsIHByZWRpY3Rpb25zCm92ZXJhbGxfc2NhZmZvbGQgPC0gbWRhdGEgJT4lCiAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCB3YXJkLCBjYXNlcykgJT4lCiAgICBncm91cF9ieSh5ZWFyLCB5ZWFyMiwgeV9udW0sIGFjZl9wZXJpb2QpICU+JQogICAgc3VtbWFyaXNlKHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAgPSBzdW0ocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCksCiAgICAgICAgICAgICAgY2FzZXMgPSBzdW0oY2FzZXMpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShpbmNfMTAwayA9IGNhc2VzL3BvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAqMTAwMDAwKSAlPiUKICAgIGxlZnRfam9pbihtZGF0YV9leHRyYXB1bG1vbmFyeSAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lCiAgICAgICAgICAgICAgICBzdW1tYXJpc2UoY2FzZXNfZXh0cmFwdWxtb25hcnkgPSBzdW0oY2FzZXMpKSkgJT4lCiAgICBtdXRhdGUoaW5jXzEwMGtfZXh0cmFwdWxtb25hcnkgPSBjYXNlc19leHRyYXB1bG1vbmFyeS9wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKjEwMDAwMCkKCmBgYAoKCiMjIyA3LiBQdWxtb25hcnkgVEIgbW9kZWwKCiMjIyMgNy4xIEZpdCB0aGUgbW9kZWwKCgpMb29rIGF0IHRoZSBtZWFuIGFuZCB2YXJpYW5jZSBvZiBjb3VudHMgKGNvdW50cyBvZiBwdWxtb25hcnkgbm90aWZpY2F0aW9ucyBhcmUgd2hhdCB3ZSBhcmUgcHJlZGljdGluZykKCmBgYHtyfQoKI01lYW4gb2YgY291bnRzIHBlciB5ZWFyCm1lYW4obWRhdGEkY2FzZXMpCiN2YXJpYW5jZSBvZiBjb3VudHMgcGVyIHllYXIKdmFyKG1kYXRhJGNhc2VzKQoKYGBgCgoKUXVpdGUgYSBiaXQgb2Ygb3Zlci1kaXNwZXJzaW9uIGhlcmUsIHNvIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBtaWdodCBiZSBhIGJldHRlciBjaG9pY2Ugb2YgZGlzdHJpYnV0aW9uYWwgZmFtaWx5IHRoYW4gUG9pc3Nvbi4KCkZpdCB0aGUgbW9kZWwgd2l0aCB0aGUgZGF0YQoKYGBge3J9CgptX3B1bG1vbmFyeSA8LSBicm0oCiAgY2FzZXMgfiAwICsgSW50ZXJjZXB0ICsgeV9udW0qYWNmX3BlcmlvZCArICgxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpICsgb2Zmc2V0KGxvZyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBtZGF0YSwKICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSwKICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQsCiAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDQsIGNvcmVzID0gNCwKICAgICAgICAgICAgICAgICAgcHJpb3IgPSBwcmlvcihub3JtYWwoMCwxKSwgY2xhc3M9YiwgY29lZiA9ICJJbnRlcmNlcHQiKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IoZ2FtbWEoMC4wMSwgMC4wMSksIGNsYXNzID0gc2hhcGUpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihub3JtYWwoMCwgMSksIGNsYXNzID0gYikgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGV4cG9uZW50aWFsKDEpLCBjbGFzcz1zZCkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGxraig0KSwgY2xhc3M9Y29yKSwKICBjb250cm9sID0gbGlzdChhZGFwdF9kZWx0YSA9IDAuOSkpCiAgCiNjaGVjayBtb2RlbCBkaWFnbm9zdGljcwpzdW1tYXJ5KG1fcHVsbW9uYXJ5KQpwbG90KG1fcHVsbW9uYXJ5KQoKcHBfY2hlY2sobV9wdWxtb25hcnksIHR5cGU9J2VjZGZfb3ZlcmxheScpCnByaW9yX3N1bW1hcnkobV9wdWxtb25hcnkpCgpgYGAKCk5pY2VyIHZlcnNpb24gb2YgdHJhY2UgcGxvdHMgZm9yIHN1cHBsZW1lbnRhbCBtYXRlcmlhbAoKYGBge3IsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0xNn0KCmFzX2RyYXdzX2RmKG1fcHVsbW9uYXJ5KSAlPiUgCiAgYmF5ZXNwbG90OjptY21jX3Jhbmtfb3ZlcmxheShwYXJzID0gdmFycyhiX0ludGVyY2VwdDpzaGFwZSksCiAgICAgICAgICAgICBmYWNldF9hcmdzID0gbGlzdChuY29sID0gNCkpICsKICBzY2FsZV9jb2xvdXJfc2NpY29fZChwYWxldHRlID0gIm1hbmFndWEiLCBuYW1lID0gIkNoYWluIikgKwogIHRoZW1lX2dnZGlzdCgpKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9zNy50aWZmIiksIHdpZHRoPTE2LCBoZWlnaHQ9MTYpCmBgYAoKTmljZXIgdmVyc2lvbiBvZiB0YWJsZSBvZiBwYXJhbWV0ZXJzIGZvciBzdXBwbGVtZW50CgpgYGB7cn0KCnN1bW1hcmlzZV9kcmF3cyhtX3B1bG1vbmFyeSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKG1lYW46ZXNzX3RhaWwpLCBjb21tYSwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHdyaXRlX2NzdihoZXJlKCJmaWd1cmVzL3MxX3RhYmxlLmNzdiIpKQoKYGBgCgoKCiMjIyMgNy4yIFN1bW1hcmlzZSBjaGFuZ2UgaW4gQ05ScwoKU3VtbWFyaXNlIHRoZSBwb3N0ZXJpb3IgaW4gZ3JhcGhpY2FsIGZvcm0KCmBgYHtyfQoKZjFiIDwtIHBsb3RfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG92ZXJhbGxfc2NhZmZvbGQsIG1vZGVsID0gbV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgb3V0Y29tZSA9IGluY18xMDBrLCBncm91cGluZ192YXI9TlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IE5BKQogIApmMWIKYGBgCgpNYWtlIHRoaXMgaW50byBhIGZpZ3VyZSBjb21iaW5lZCB3aXRoIHRoZSBtYXAgb2YgZW1waXJpY2FsIGRhdGEKCmBgYHtyfQoKZjFhIDwtIHN0X2FzX3NmKGxlZnRfam9pbih3YXJkX2luYywgZ2xhc2dvd193YXJkc18xOTUxKSkgJT4lCiAgZmlsdGVyKHRiX3R5cGU9PSJQdWxtb25hcnkiKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbD1pbmNfMTAwayksIGNvbG91cj0iZ3JleTk4IiwgbHdkPTAuMDEpICsKICBmYWNldF93cmFwKHllYXJ+LiwgbmNvbCA9IDcpICsKICBzY2FsZV9maWxsX3NjaWNvKG5hbWU9IkNOUiAocGVyIDEwMCwwMDApIiwKICAgICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gImFjdG9uIiwgZGlyZWN0aW9uID0gLTEpICsKICB0aGVtZV9ncmV5KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgICNsZWdlbmQua2V5LndpZHRoID0gdW5pdCgxLCAiY20iKSwKICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLjUsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpCgooZjFhIC8gZjFiKSArIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gIkEiKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZjEudGlmZiIpLCB3aWR0aD03LCBoZWlnaHQ9OCkKCmBgYAoKU3VtbWFyeSBvZiBjaGFuZ2UgaW4gbm90aWZpY2F0aW9ucyBudW1lcmljYWxseQoKYGBge3J9CgpvdmVyYWxsX2NoYW5nZSA8LSBzdW1tYXJpc2VfY2hhbmdlKG1vZGVsX2RhdGE9b3ZlcmFsbF9zY2FmZm9sZCwgbW9kZWw9bV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3I9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGEgPSBOQSkKCiN3YW50IHRvIGtlZXAgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGhlcmUKdG9rZWVwIDwtIGMoInBlYWtfc3VtbWFyeSIsICJsZXZlbF9zdW1tYXJ5IiwgInNsb3BlX3N1bW1hcnkiKQoKI3N1bW1hcnkgbWVhc3VyZXMgaW4gYSB0YWJsZQpvdmVyYWxsX2NoYW5nZSAlPiUKICBrZWVwKChuYW1lcyguKSAlaW4lIHRva2VlcCkpICU+JQogIGJpbmRfcm93cygpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKICAKYGBgCgoKIyMjIyA3LjMgQ29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwKCk51bWJlcnMgb2YgcHVsbW9uYXJ5IFRCIGNhc2VzIGF2ZXJ0ZWQgY29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwgcGVyIHllYXIuCgpgYGB7cn0KCm92ZXJhbGxfcHVsbW9uYXJ5X2NvdW50ZXJmIDwtIGNhbGN1bGF0ZV9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCwgbW9kZWw9bV9wdWxtb25hcnksIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKQoKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsIGRpZmZfaW5jMTAwazpkaWZmX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocnJfaW5jMTAwazpycl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMDEpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpUb3RhbCBwdWxtb25hcnkgVEIgY2FzZXMgYXZlcnRlZCBiZXR3ZWVuIDE5NTggYW5kIDE5NjMKCmBgYHtyfQoKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0X292ZXJhbGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKIyMjIyA3LjQgQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrLCBSUi5sZXZlbCwgYW5kIFJSLnNsb3BlCgpXaGF0IGFyZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gcGVhaywgbGV2ZWwsIGFuZCBzbG9wZT8KCmBgYHtyfQoKI1JSLnBlYWsgaGlzdG9ncmFtCmEgPC0gb3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9ZXN0aW1hdGUpLCBmaWxsPSJkYXJrYmx1ZSIsIGNvbG91cj0iZGFya2JsdWUiLCBhbHBoYT0wLjMpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpICsKICBsYWJzKHg9IlJSLnBlYWsiLAogICAgICAgeT0iIikKCiNSUi4gbGV2ZWwgaGlzdG9ncmFtCmIgPC0gb3ZlcmFsbF9jaGFuZ2UkbGV2ZWxfZHJhd3MgICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeD1lc3RpbWF0ZSksIGZpbGw9ImRhcmtibHVlIiwgY29sb3VyPSJkYXJrYmx1ZSIsIGFscGhhPTAuMykrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iUlIubGV2ZWwiLAogICAgICAgeT0iIikKCiNSUi5zbG9wZSBoaXN0b2dyYW0KYyA8LSBvdmVyYWxsX2NoYW5nZSRzbG9wZV9kcmF3cyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9ZXN0aW1hdGUpLCBmaWxsPSJkYXJrYmx1ZSIsIGNvbG91cj0iZGFya2JsdWUiLCBhbHBoYT0wLjMpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsgIAogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iUlIuc2xvcGUiLAogICAgICAgeT0iIikKCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5sZXZlbApjb3JfcnJfcGVha19ycl9sZXZlbCA8LSByb3VuZChjb3IocGx1Y2sob3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJGxldmVsX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5zbG9wZQpjb3JfcnJfcGVha19ycl9zbG9wZSA8LSByb3VuZChjb3IocGx1Y2sob3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5sZXZlbCBhbmQgUlIuc2xvcGUKY29yX3JyX2xldmVsX3JyX3Nsb3BlIDwtIHJvdW5kKGNvcihwbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgoKI3Bsb3Qgb2YgY29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5sZXZlbApkIDwtIGJpbmRfY29scyhSUi5wZWFrPXBsdWNrKG92ZXJhbGxfY2hhbmdlJHBlYWtfZHJhd3MkZXN0aW1hdGUpLCAKICAgICAgICAgIFJSLmxldmVsID1wbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSkpICU+JQogIGdncGxvdChhZXMoeT1SUi5wZWFrLCB4ID0gUlIubGV2ZWwpKSArCiAgZ2VvbV9oZXgoKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UsIGNvbG91cj0iZmlyZWJyaWNrIiwgbWV0aG9kID0gImxtIikgKwogIGdlb21fdGV4dChhZXMoeT0yLjIsIHg9MC41OCwgbGFiZWw9Y29yX3JyX3BlYWtfcnJfbGV2ZWwpLCBjb2xvdXI9ImZpcmVicmljayIpICArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCiNwbG90IG9mIGNvcnJlbGF0aW9uIGJldHdlZW4gUlIucGVhayBhbmQgUlIuc2xvcGUKZSA8LSBiaW5kX2NvbHMoUlIucGVhaz1wbHVjayhvdmVyYWxsX2NoYW5nZSRwZWFrX2RyYXdzJGVzdGltYXRlKSwgCiAgICAgICAgICBSUi5zbG9wZSA9cGx1Y2sob3ZlcmFsbF9jaGFuZ2Ukc2xvcGVfZHJhd3MkZXN0aW1hdGUpKSAlPiUKICBnZ3Bsb3QoYWVzKHk9UlIucGVhaywgeCA9IFJSLnNsb3BlKSkgKwogIGdlb21faGV4KCkgKwogIGdlb21fc21vb3RoKHNlPUZBTFNFLCBjb2xvdXI9ImZpcmVicmljayIsIG1ldGhvZCA9ICJsbSIpICsKICBnZW9tX3RleHQoYWVzKHk9Mi4xLCB4PTAuNjUsIGxhYmVsPWNvcl9ycl9wZWFrX3JyX3Nsb3BlKSwgY29sb3VyPSJmaXJlYnJpY2siKSAgKwogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgojcGxvdCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIFJSLmxldmVsIGFuZCBSUi5zbG9wZQpmIDwtIGJpbmRfY29scyhSUi5sZXZlbD1wbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSksIAogICAgICAgICAgUlIuc2xvcGUgPXBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSkgJT4lCiAgZ2dwbG90KGFlcyh5PVJSLmxldmVsLCB4ID0gUlIuc2xvcGUpKSArCiAgZ2VvbV9oZXgoKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UsIGNvbG91cj0iZmlyZWJyaWNrIiwgbWV0aG9kID0gImxtIikgKwogIGdlb21fdGV4dChhZXMoeT0wLjc1LCB4PTAuNjUsIGxhYmVsPWNvcl9ycl9sZXZlbF9ycl9zbG9wZSksIGNvbG91cj0iZmlyZWJyaWNrIikgICsgIAogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgoKKHBsb3Rfc3BhY2VyKCkgKyBwbG90X3NwYWNlcigpICsgYykgLwogIChwbG90X3NwYWNlcigpICsgYiArIGYpIC8KICAoYSArIGQgKyBlKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczkudGlmZiIpLCB3aWR0aD04LCBoZWlnaHQ9OCkKCgoKYGBgCgoKIyMjIyA3LjUgV2FyZCBsZXZlbCBwdWxtb25hcnkgVEIgZXN0aW1hdGVzCgpQbG90IHRoZSBjb3VudGVyZmFjdHVhbCBhdCB3YXJkIGxldmVsCgpgYGB7cn0KCnBsb3RfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG1kYXRhLCBtb2RlbD1tX3B1bG1vbmFyeSwgb3V0Y29tZSA9IGluY18xMDBrLCBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgCiAgICAgICAgICAgICAgICAgICAgZ3JvdXBpbmdfdmFyID0gd2FyZCwgd2FyZCwgcmVfZm9ybXVsYT0gfigxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCw1MDApKQogIApnZ3NhdmUoaGVyZSgiZmlndXJlcy9zNi50aWZmIiksIHdpZHRoPTE2LCBoZWlnaHQ9MTIpCgpgYGAKClN1bW1hcnkgb2YgY2hhbmdlIGluIG5vdGlmaWNhdGlvbnMgYXQgd2FyZCBsZXZlbAoKYGBge3J9Cgp3YXJkX2NoYW5nZSA8LSBzdW1tYXJpc2VfY2hhbmdlKG1vZGVsX2RhdGE9bWRhdGEsIG1vZGVsPW1fcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yPXBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIGdyb3VwaW5nX3Zhcj13YXJkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0gfigxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpKQoKI3dhbnQgdG8ga2VlcCB0aGUgc3VtbWFyeSBlc3RpbWF0ZXMgaGVyZQp0b2tlZXAgPC0gYygicGVha19zdW1tYXJ5IiwgImxldmVsX3N1bW1hcnkiLCAic2xvcGVfc3VtbWFyeSIpCgojc3VtbWFyeSBtZWFzdXJlcyBpbiBhIHRhYmxlCndhcmRfY2hhbmdlICU+JQogIGtlZXAoKG5hbWVzKC4pICVpbiUgdG9rZWVwKSkgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGVzdGltYXRlOi51cHBlciksIG51bWJlciwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIGRhdGF0YWJsZSgpCgoKI3Bsb3QgdGhlc2UgaW4gYSBmaWd1cmUKd2FyZF9lZmZlY3RzIDwtIHdhcmRfY2hhbmdlICU+JQogIGtlZXAoKG5hbWVzKC4pICVpbiUgdG9rZWVwKSkgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgYmluZF9yb3dzKG92ZXJhbGxfY2hhbmdlJHBlYWtfc3VtbWFyeSkgJT4lCiAgYmluZF9yb3dzKG92ZXJhbGxfY2hhbmdlJGxldmVsX3N1bW1hcnkpICU+JQogIGJpbmRfcm93cyhvdmVyYWxsX2NoYW5nZSRzbG9wZV9zdW1tYXJ5KSAlPiUKICBtdXRhdGVfYXQoLnZhcnMgPSB2YXJzKGVzdGltYXRlOi51cHBlciksIAogICAgICAgICAgICAuZnVucyA9IGZ1bnMoYXMubnVtZXJpYykpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIG11dGF0ZShlc3RpbWF0ZSA9IGFzLmRvdWJsZShlc3RpbWF0ZSkpICU+JQogIGZ1bGxfam9pbihnbGFzZ293X3dhcmRzXzE5NTEpICU+JSAKICBtdXRhdGUod2FyZDIgPSBwYXN0ZTAod2FyZF9udW1iZXIsICIuICIsIHdhcmQpKSAlPiUKICBtdXRhdGUod2FyZDIgPSBjYXNlX3doZW4oaXMubmEod2FyZCkgfiAiT3ZlcmFsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHdhcmQyKSkgJT4lCiAgc3RfYXNfc2YoKSAKCiNmdW5jdGlvbiBmb3IgcGxvdHRpbmcgY2hvcm9wbGV0aCBtYXBzCnBsb3Rfd2FyZF9lZmZlY3QgPC0gZnVuY3Rpb24oZGF0YSwgbWVhc3VyZSl7CiAge3tkYXRhfX0gJT4lCiAgZmlsdGVyKG1lYXN1cmUgPT0ge3ttZWFzdXJlfX0pICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKGFlcyhmaWxsPWVzdGltYXRlKSkgKwogIGdlb21fc2ZfbGFiZWwoYWVzKGxhYmVsID0gd2FyZF9udW1iZXIpLCBzaXplPTMsIGZpbGw9TkEsIGxhYmVsLnNpemUgPSBOQSwgY29sb3VyPSJibGFjayIpICsKICBzY2FsZV9maWxsX3NjaWNvKHRyYW5zPSJsb2ciLCBwYWxldHRlID0gInJvbWEiLCBtaWRwb2ludCA9IDAsIGxpbWl0cz1jKDAuNSwyLjI1KSwKICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAuNSwgMC43NSwgMSwgMS41LCAyLCAyLjUpLCBsYWJlbHMgPSBjKDAuNSwgMC43NSwgMSwgMS41LCAyLCAyLjUpLAogICAgICAgICAgICAgICAgICAgbmFtZT0iUmVsYXRpdmUgcmF0ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArCiAgICBsYWJzKHg9IiIsIHk9IiIpCn0KCiNmdW5jdGlvbiBmb3IgcGxvdHRpbmcgY2F0YXBpbGxlciBwbG90cwpwbG90X3dhcmRfY2F0IDwtIGZ1bmN0aW9uKGRhdGEsIG1lYXN1cmUsIHNjYWxlKXsKCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PTEpLCBsaW5ldHlwZT0yKSArCiAgICBnZW9tX3BvaW50cmFuZ2UoZGF0YSA9IHt7ZGF0YX19ICU+JSAgICAgCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIobWVhc3VyZT09e3ttZWFzdXJlfX0pICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFpcy5uYSh3YXJkKSksCiAgICAgICAgICAgICAgICAgICAgYWVzKHk9ZXN0aW1hdGUsIHltaW49Lmxvd2VyLCB5bWF4PS51cHBlciwgCiAgICAgICAgICAgICAgICAgICAgICB4PWZjdF9yZW9yZGVyKHdhcmQyLCBlc3RpbWF0ZSksIGNvbG91cj1lc3RpbWF0ZSkpICsKICAgIGdlb21fcG9pbnRyYW5nZShkYXRhID0ge3tkYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKG1lYXN1cmU9PXt7bWVhc3VyZX19KSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihpcy5uYSh3YXJkKSksCiAgICAgICAgICAgICAgICAgICAgYWVzKHk9ZXN0aW1hdGUsIHltaW49Lmxvd2VyLCB5bWF4PS51cHBlciwgCiAgICAgICAgICAgICAgICAgICAgICB4PXdhcmQyKSwgY29sb3VyPSJibGFjayIpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV9jb2xvdXJfc2NpY28odHJhbnM9ImxvZyIsIHBhbGV0dGUgPSAicm9tYSIsIG1pZHBvaW50ID0gMCwgbGltaXRzPWMoMC41LDIuMjUpLCAKICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAuNSwgMC43NSwgMSwgMS41LCAyLCAyLjUpLCBsYWJlbHMgPSBjKDAuNSwgMC43NSwgMSwgMS41LCAyLCAyLjUpLAogICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlJlbGF0aXZlIHJhdGUiKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoKSArCiAgICB0aGVtZV9nZ2Rpc3QoKSAgKwogICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogICAgbGFicyh4ID0gIiIsCiAgICAgICAgIHkgPSAiUmVsYXRpdmUgcmF0ZSAoOTUlIFVJKSIpKwogICAgZ3VpZGVzKHggPSAiYXhpc190cnVuY2F0ZWQiLCB5ID0gImF4aXNfdHJ1bmNhdGVkIikKfQoKCgp3YXJkX3BlYWtfaSA8LSBwbG90X3dhcmRfZWZmZWN0KGRhdGEgPSB3YXJkX2VmZmVjdHMsIG1lYXN1cmUgPSAiUlIucGVhayIpICsgZ2d0aXRsZSgiUGVhayBlZmZlY3QiKQp3YXJkX2xldmVsX2kgPC0gcGxvdF93YXJkX2VmZmVjdChkYXRhID0gd2FyZF9lZmZlY3RzLCBtZWFzdXJlID0gIlJSLmxldmVsIikgKyBnZ3RpdGxlKCJMZXZlbCBlZmZlY3QiKQp3YXJkX3Nsb3BlX2kgPC0gcGxvdF93YXJkX2VmZmVjdChkYXRhID0gd2FyZF9lZmZlY3RzLCBtZWFzdXJlID0gIlJSLnNsb3BlIikgKyBnZ3RpdGxlKCJTbG9wZSBlZmZlY3QiKQoKd2FyZF9wZWFrX2lpIDwtIHBsb3Rfd2FyZF9jYXQoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5wZWFrIikgKyBnZ3RpdGxlKCJQZWFrIGVmZmVjdCIpCndhcmRfbGV2ZWxfaWkgPC0gcGxvdF93YXJkX2NhdChkYXRhID0gd2FyZF9lZmZlY3RzLCBtZWFzdXJlID0gIlJSLmxldmVsIikgKyBnZ3RpdGxlKCJMZXZlbCBlZmZlY3QiKQp3YXJkX3Nsb3BlX2lpIDwtIHBsb3Rfd2FyZF9jYXQoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5zbG9wZSIpICsgZ2d0aXRsZSgiU2xvcGUgZWZmZWN0IikKCnM0IDwtICh3YXJkX3BlYWtfaSArIHdhcmRfbGV2ZWxfaSArIHdhcmRfc2xvcGVfaSkgLwogICh3YXJkX3BlYWtfaWkgKyB3YXJkX2xldmVsX2lpICsgd2FyZF9zbG9wZV9paSkKCnM0W1sxXV0gPC0gczRbWzFdXSArIHBsb3RfbGF5b3V0KHRhZ19sZXZlbCA9ICduZXcnKQpzNFtbMl1dIDwtIHM0W1syXV0gKyBwbG90X2xheW91dCh0YWdfbGV2ZWwgPSAnbmV3JykKczQgKyBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9IGMoJ0EnLCAnMScpKSArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICdjb2xsZWN0JykgJgogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywKICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgzLCAiY20iKSkKCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9mMi50aWZmIiksIHdpZHRoID0gMTYsIGhlaWdodD0xMikKCmBgYAoKQ2FsY3VsYXRlIHRoZSBjb3VudGVyZmFjdHVhbCBwZXIgd2FyZAoKYGBge3J9Cgp3YXJkX3B1bG1vbmFyeV9jb3VudGVyZiA8LSBjYWxjdWxhdGVfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG1kYXRhLCBtb2RlbD1tX3B1bG1vbmFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9IHdhcmQsIHJlX2Zvcm11bGE9figxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpKQoKd2FyZF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsIGRpZmZfaW5jMTAwazpkaWZmX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocnJfaW5jMTAwazpycl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMDEpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpPdmVyYWxsIGNvdW50ZXJmYWN0dWFsIHBlciB3YXJkCgpgYGB7cn0KCndhcmRfcHVsbW9uYXJ5X2NvdW50ZXJmJGNvdW50ZXJfcG9zdF9vdmVyYWxsICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgZGF0YXRhYmxlKCkKCmBgYAoKCgojIyMgOC4gRXh0cmEtcHVsbW9uYXJ5IFRCIG5vdGlmaWNhdGlvbnMKCk5vdyB3ZSB3aWxsIG1vZGVsIHRoZSBleHRyYS1wdWxtb25hcnkgVEIgbm90aWZpY2F0aW9uIHJhdGUuIFN0cnVnZ2xpbmcgYSBiaXQgd2l0aCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbCwgc28gcmV2ZXJ0IHRvIFBvaXNzb24uCgojIyMjIDguMSBGaXQgdGhlIG1vZGVsCgpgYGB7cn0KCm1fZXh0cmFwdWxtb25hcnkgPC0gYnJtKAogIGNhc2VzIH4gMSArIHlfbnVtKmFjZl9wZXJpb2QgKyAoMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSArIG9mZnNldChsb2cocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCkpLAogICAgICAgICAgICAgICAgICBkYXRhID0gbWRhdGFfZXh0cmFwdWxtb25hcnksCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCksCiAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0LAogICAgICAgICAgICAgICAgICBjaGFpbnMgPSA0LCBjb3JlcyA9IDQsCiAgICAgICAgICAgICAgICAgIHByaW9yID0gcHJpb3Iobm9ybWFsKDAsMTAwMCksIGNsYXNzID0gSW50ZXJjZXB0KSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IoZ2FtbWEoMC4wMSwgMC4wMSksIGNsYXNzID0gc2hhcGUpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihub3JtYWwoMCwgMSksIGNsYXNzID0gYikgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGV4cG9uZW50aWFsKDEpLCBjbGFzcz1zZCkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGxraigyKSwgY2xhc3M9Y29yKSkKCnN1bW1hcnkobV9leHRyYXB1bG1vbmFyeSkKcGxvdChtX2V4dHJhcHVsbW9uYXJ5KQpwcF9jaGVjayhtX2V4dHJhcHVsbW9uYXJ5LCB0eXBlPSdlY2RmX292ZXJsYXknKQoKYGBgCgojIyMjIDguMiBTdW1tYXJ5IG9mIGNoYW5nZQoKU3VtbWFyaXNlIGluIHBsb3QKCmBgYHtyfQpwbG90X2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBvdmVyYWxsX3NjYWZmb2xkICU+JSBmaWx0ZXIoeWVhcjw9MTk2MSksIG1vZGVsPW1fZXh0cmFwdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCBvdXRjb21lPWluY18xMDBrX2V4dHJhcHVsbW9uYXJ5LCByZV9mb3JtdWxhID0gTkEpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDUwKSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczEwLnRpZmYiKSwgd2lkdGg9MTApCgpgYGAKClN1bW1hcmlzZSBudW1lcmljYWxseS4KCmBgYHtyfQoKb3ZlcmFsbF9jaGFuZ2VfZXh0cmFwdWxtb25hcnkgPC0gc3VtbWFyaXNlX2NoYW5nZShtb2RlbF9kYXRhPW92ZXJhbGxfc2NhZmZvbGQsIG1vZGVsPW1fZXh0cmFwdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3I9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGEgPSBOQSkKCiN3YW50IHRvIGtlZXAgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGhlcmUKdG9rZWVwIDwtIGMoInBlYWtfc3VtbWFyeSIsICJsZXZlbF9zdW1tYXJ5IiwgInNsb3BlX3N1bW1hcnkiKQoKI3N1bW1hcnkgbWVhc3VyZXMgaW4gYSB0YWJsZQpvdmVyYWxsX2NoYW5nZV9leHRyYXB1bG1vbmFyeSAlPiUKICBrZWVwKG5hbWVzKC4pICVpbiUgdG9rZWVwKSAlPiUKICBiaW5kX3Jvd3MoKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoZXN0aW1hdGU6LnVwcGVyKSwgbnVtYmVyLCBhY2N1cmFjeT0wLjAxKSkgJT4lCiAgc2VsZWN0KG1lYXN1cmUsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZGF0YXRhYmxlKCkKCmBgYAoKIyMjIyA4LjMgQ29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwKCk51bWJlcnMgb2YgZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGF2ZXJ0ZWQgb3ZlcmFsbC4KCmBgYHtyfQoKb3ZlcmFsbF9lcF9jb3VudGVyZiA8LSBjYWxjdWxhdGVfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG1kYXRhX2V4dHJhcHVsbW9uYXJ5LCBtb2RlbD1tX2V4dHJhcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCkKCm92ZXJhbGxfZXBfY291bnRlcmYkY291bnRlcl9wb3N0ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsIGRpZmZfaW5jMTAwazpkaWZmX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocnJfaW5jMTAwazpycl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMDEpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKClRvdGFsIGV4dHJhcHVsbW9uYXJ5IFRCIGNhc2VzIGF2ZXJ0ZWQgYmV0d2VlbiAxOTU4IGFuZCAxOTYzCgpgYGB7cn0KCm92ZXJhbGxfZXBfY291bnRlcmYkY291bnRlcl9wb3N0X292ZXJhbGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKTWFrZSBpbnRvIFRhYmxlIDIKCgpgYGB7cn0KYmluZF9yb3dzKApvdmVyYWxsX3B1bG1vbmFyeV9jb3VudGVyZiRjb3VudGVyX3Bvc3QgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciwgZGlmZl9pbmMxMDBrOmRpZmZfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhycl9pbmMxMDBrOnJyX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4wMSkpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgbXV0YXRlKG1vZGVsID0gIlBUQl93YXJkIiksCgpvdmVyYWxsX3B1bG1vbmFyeV9jb3VudGVyZiRjb3VudGVyX3Bvc3Rfb3ZlcmFsbCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZShtb2RlbCA9ICJQVEJfb3ZlcmFsbCIpLAoKb3ZlcmFsbF9lcF9jb3VudGVyZiRjb3VudGVyX3Bvc3QgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciwgZGlmZl9pbmMxMDBrOmRpZmZfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhycl9pbmMxMDBrOnJyX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4wMSkpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgbXV0YXRlKG1vZGVsID0gIkVQVEIiKSwKCm92ZXJhbGxfZXBfY291bnRlcmYkY291bnRlcl9wb3N0X292ZXJhbGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUobW9kZWwgPSAiRVBUQiBvdmVyYWxsIikKCikgJT4lCiAgc2VsZWN0KG1vZGVsLCB5ZWFyLCBkaWZmX2luYzEwMGssIGRpZmZfaW5jMTAway5sb3dlcjpycl9pbmMxMDBrLnVwcGVyLCAKICAgICAgICAgY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLAogICAgICAgICBwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpICU+JQogIHRyYW5zbXV0ZShtb2RlbD1tb2RlbCwgeWVhcj15ZWFyLAogICAgICAgICAgICBkaWZmX2NuciA9IGdsdWUoIntkaWZmX2luYzEwMGt9ICh7ZGlmZl9pbmMxMDBrLmxvd2VyfSB0byB7ZGlmZl9pbmMxMDBrLnVwcGVyfSkiKSwKICAgICAgICAgICAgcnIgPSBnbHVlKCJ7cnJfaW5jMTAwa30gKHtycl9pbmMxMDBrLmxvd2VyfSB0byB7cnJfaW5jMTAway51cHBlcn0pIiksCiAgICAgICAgICAgIGNhc2VzX2F2ZXJ0ZWQgPSBnbHVlKCJ7Y2FzZXNfYXZlcnRlZH0gKHtjYXNlc19hdmVydGVkLmxvd2VyfSB0byB7Y2FzZXNfYXZlcnRlZC51cHBlcn0pIiksCiAgICAgICAgICAgIHBjdF9jaGFuZ2UgPSBnbHVlKCJ7cGN0X2NoYW5nZX0gKHtwY3RfY2hhbmdlLmxvd2VyfSB0byB7cGN0X2NoYW5nZS51cHBlcn0pIikpICU+JQogIHdyaXRlX2NzdihoZXJlKCJmaWd1cmVzL3RhYmxlMi5jc3YiKSkKCgoKYGBgCgoKCgojIyMjIDguNCBXYXJkLWxldmVsIGV4dHJhLXB1bG1vbmFyeSBzdW1tYXJpZXMKCldhcmQtbGV2ZWwgZXh0cmEtcHVsbW9uYXJ5IGVzdGltYXRlcyBpbiBncmFwaGljYWwgZm9ybS4KCmBgYHtyfQoKcGxvdF9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gbWRhdGFfZXh0cmFwdWxtb25hcnksIG1vZGVsPW1fZXh0cmFwdWxtb25hcnksIG91dGNvbWUgPSBpbmNfMTAwaywgCiAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIGdyb3VwaW5nX3ZhciA9IHdhcmQscmVfZm9ybXVsYSA9fih5X251bSphY2ZfcGVyaW9kIHwgd2FyZCksIAogICAgICAgICAgICAgICAgICAgIHdhcmQpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz0gYygwLDc1KSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczExLnRpZmYiKSwgd2lkdGg9MTAsIGhlaWdodD0xMikKCmBgYAoKTnVtZXJpY2FsIHN1bW1hcnkuCgpgYGB7cn0KCndhcmRfY2hhbmdlX2V4dHJhcHVsbW9uYXJ5IDwtIHN1bW1hcmlzZV9jaGFuZ2UobW9kZWxfZGF0YSA9IG1kYXRhX2V4dHJhcHVsbW9uYXJ5LCBtb2RlbCA9IG1fZXh0cmFwdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCBncm91cGluZ192YXI9d2FyZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0gfih5X251bSphY2ZfcGVyaW9kIHwgd2FyZCkpIAoKI3dhbnQgdG8ga2VlcCB0aGUgc3VtbWFyeSBlc3RpbWF0ZXMgaGVyZQp0b2tlZXAgPC0gYygicGVha19zdW1tYXJ5IiwgImxldmVsX3N1bW1hcnkiLCAic2xvcGVfc3VtbWFyeSIpCgojc3VtbWFyeSBtZWFzdXJlcyBpbiBhIHRhYmxlCndhcmRfY2hhbmdlX2V4dHJhcHVsbW9uYXJ5ICAlPiUKICBrZWVwKG5hbWVzKC4pICVpbiUgdG9rZWVwKSAlPiUKICBiaW5kX3Jvd3MoKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoZXN0aW1hdGU6LnVwcGVyKSwgbnVtYmVyLCBhY2N1cmFjeT0wLjAxKSkgJT4lCiAgc2VsZWN0KG1lYXN1cmUsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgoKYGBgCgoKIyMjIDkuIEFnZS1zZXggbW9kZWwKCiMjIyMgOS4xIEZJdCB0aGUgbW9kZWwKCkZpdCB0aGUgbW9kZWwKCihOb3QgcmV3cml0dGVuIHRoZSBmdW5jdGlvbnMgZm9yIHRoaXMgeWV0KQoKYGBge3J9CgptZGF0YV9hZ2Vfc2V4IDwtIGNhc2VzX2J5X2FnZV9zZXggJT4lCiAgZmlsdGVyKHRiX3R5cGU9PSJQdWxtb25hcnkiKSAlPiUKICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbih5ZWFyICVpbiUgYygxOTUwOjE5NTYpIH4gImEuIHByZS1hY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTcpIH4gImIuIGFjZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciAlaW4lIGMoMTk1ODoxOTYzKSB+ICJjLiBwb3N0LWFjZiIpKSAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgZ3JvdXBfYnkoYWdlLCBzZXgpICU+JQogIG11dGF0ZSh5X251bSA9IHJvd19udW1iZXIoKSkgJT4lCiAgdW5ncm91cCgpCgptX2FnZV9zZXggPC0gYnJtKAogIGNhc2VzIH4geV9udW0gKyAoYWNmX3BlcmlvZCkqKGFnZSpzZXgpICsgKGFjZl9wZXJpb2Q6eV9udW0pKihhZ2Uqc2V4KSwKICAgICAgICAgICAgICAgICAgZGF0YSA9IG1kYXRhX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCksCiAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0LAogICAgICAgICAgICAgICAgICBjaGFpbnMgPSA0LCBjb3JlcyA9IDQsIAogICAgICAgICAgICAgICAgICBwcmlvciA9IHByaW9yKG5vcm1hbCgwLDEpLCBjbGFzcyA9IEludGVyY2VwdCkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGdhbW1hKDAuMDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3Iobm9ybWFsKDAsIDEpLCBjbGFzcyA9IGIpKQoKc3VtbWFyeShtX2FnZV9zZXgpCnBsb3QobV9hZ2Vfc2V4KQpwcF9jaGVjayhtX2FnZV9zZXgsIHR5cGU9J2VjZGZfb3ZlcmxheScpCgpgYGAKClN1bW1hcmlzZSBwb3N0ZXJpb3IKCgpgYGB7cn0KCiNwb3N0ZXJpb3IgZHJhd3MsIGFuZCBzdW1tYXJpc2UKYWdlX3NleF9zdW1tYXJ5IDwtIG1kYXRhX2FnZV9zZXggJT4lCiAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWNmX3BlcmlvZCwgYWdlLCBzZXgpICU+JQogIGFkZF9lcHJlZF9kcmF3cyhtX2FnZV9zZXgpICU+JQogIGdyb3VwX2J5KHllYXIyLCBhY2ZfcGVyaW9kLCBhZ2UsIHNleCkgJT4lCiAgbWVhbl9xaSgpICU+JQogIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJCZWZvcmUgSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iYy4gcG9zdC1hY2YiIH4gIlBvc3QgSW50ZXJ2ZW50aW9uIikpCgojY3JlYXRlIHRoZSBjb3VudGVyZmFjdHVhbCAobm8gaW50ZXJ2ZW50aW9uKSwgYW5kIHN1bW1hcmlzZQphZ2Vfc2V4X2NvdW50ZXJmYWN0IDwtIAogIHRpYmJsZSh5ZWFyID0gbWRhdGFfYWdlX3NleCR5ZWFyLAogICAgICAgICB5ZWFyMiA9IG1kYXRhX2FnZV9zZXgkeWVhcjIsCiAgICAgICAgIHlfbnVtID0gbWRhdGFfYWdlX3NleCR5X251bSwKICAgICAgICAgYWdlID0gbWRhdGFfYWdlX3NleCRhZ2UsCiAgICAgICAgIHNleCA9IG1kYXRhX2FnZV9zZXgkc2V4LAogICAgICAgICBhY2ZfcGVyaW9kID0gZmFjdG9yKCJhLiBwcmUtYWNmIikpICU+JQogIGFkZF9lcHJlZF9kcmF3cyhtX2FnZV9zZXgpICU+JQogIGdyb3VwX2J5KHllYXIyLCBhY2ZfcGVyaW9kLCBhZ2UsIHNleCkgJT4lCiAgbWVhbl9xaSgpICU+JQogIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJCZWZvcmUgSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iYy4gcG9zdC1hY2YiIH4gIlBvc3QgSW50ZXJ2ZW50aW9uIikpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0gIk0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0gIkYiIH4gIkZlbWFsZSIpKSAKCgoKYWdlX3NleF9zdW1tYXJ5ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0gIk0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0gIkYiIH4gIkZlbWFsZSIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW49LmVwcmVkLmxvd2VyLCB5bWF4PS5lcHJlZC51cHBlciwgeD15ZWFyMiwgZ3JvdXAgPSBhY2ZfcGVyaW9kLCBmaWxsPWFjZl9wZXJpb2QpLCBhbHBoYT0wLjUpICsKICBnZW9tX3JpYmJvbihkYXRhID0gYWdlX3NleF9jb3VudGVyZmFjdCAlPiUgZmlsdGVyKHllYXI+PTE5NTYpLCAKICAgICAgICAgICAgICBhZXMoeW1pbj0uZXByZWQubG93ZXIsIHltYXg9LmVwcmVkLnVwcGVyLCB4PXllYXIyLCBmaWxsPSJDb3VudGVyZmFjdHVhbCIpLCBhbHBoYT0wLjUpICsKICBnZW9tX2xpbmUoZGF0YSA9IGFnZV9zZXhfY291bnRlcmZhY3QgJT4lIGZpbHRlcih5ZWFyPj0xOTU2KSwgCiAgICAgICAgICAgICAgYWVzKHk9LmVwcmVkLCB4PXllYXIyLCBjb2xvdXI9IkNvdW50ZXJmYWN0dWFsIikpICsKICBnZW9tX2xpbmUoYWVzKHk9LmVwcmVkLCB4PXllYXIyLCBncm91cD1hY2ZfcGVyaW9kLCAgY29sb3VyPWFjZl9wZXJpb2QpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKGFnZSA9IGNhc2Vfd2hlbihhZ2U9PSIwMF8wNSIgfiAiMCB0byA1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIwNl8xNSIgfiAiMDYgdG8gMTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjE2XzI1IiB+ICIxNiB0byAyNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMjZfMzUiIH4gIjI2IHRvIDM1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIzNl80NSIgfiAiMzYgdG8gNDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjQ2XzU1IiB+ICI0NiB0byA1NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNTZfNjUiIH4gIjU2IHRvIDY1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI2NSsiIH4gIjY1ICYgdXAgeSIpKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09ICJNIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNleD09ICJGIiB+ICJGZW1hbGUiKSkgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJiLiBhY2YiIH4gIkNvdW50ZXJmYWN0dWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSklPiUKICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZDIgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09IkJlZm9yZSBJbnRlcnZlbnRpb24iIH4gIlByZS1BQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJDb3VudGVyZmFjdHVhbCIgfiAiQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iUG9zdCBJbnRlcnZlbnRpb24iIH4gIlBvc3QtQUNGIikpLCAKICBhZXMoeT1jYXNlcywgeD15ZWFyMiwgc2hhcGU9ZmN0X3JlbGV2ZWwoYWNmX3BlcmlvZDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHJlLUFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQb3N0LUFDRiIpKSwgc2l6ZT0yKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIGZhY2V0X2dyaWQoZmN0X3JlbGV2ZWwoYWdlLCAiNjUgJiB1cCB5IiwgIjU2IHRvIDY1eSIsICI0NiB0byA1NXkiLCAiMzYgdG8gNDV5IiwgIjI2IHRvIDM1eSIsICIxNiB0byAyNXkiLCAiMDYgdG8gMTV5IiwgIjAgdG8gNXkiKX5zZXgsIAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjREUwRDkyIiwgImdyZXk1MCIsICIjNEQ2Q0ZBIikgLCBuYW1lPSJNb2RlbCBlc3RpbWF0ZXM6IikgKwogIHNjYWxlX3NoYXBlX2Rpc2NyZXRlKG5hbWU9IkVtcHJpY2FsIGRhdGEgKHBlcmlvZCk6IiwgbmEudHJhbnNsYXRlID0gRikgKwogIGxhYnMoCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJDYXNlIG5vdGlmaWNhdGlvbnMgKG4pIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsICAgICAgICAgIAogICAgICAgIGxlZ2VuZC5ib3g9InZlcnRpY2FsIiwgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczEzLnRpZmYiKSwgaGVpZ2h0PTEwKQoKYGBgCgojIyMjIDkuMiBTdW1tYXJ5IG9mIGltcGFjdCBvZiBpbnRlcnZlbnRpb24KCkNhbGN1bGF0ZSBzdW1tYXJ5IGVmZmVjdHMKCmBgYHtyfQoKI1BlYWsKb3V0X2FnZV9zZXhfMSA8LSBjcm9zc2luZyhtZGF0YV9hZ2Vfc2V4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDgpLAogICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYi4gYWNmIikpCgpwZWFrX2RyYXdzX2FnZV9zZXggPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXRfYWdlX3NleF8xLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSBtX2FnZV9zZXgpICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoLmVwcmVkKS9maXJzdCguZXByZWQpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnBlYWsiKQogIApwZWFrX3N1bW1hcnlfYWdlX3NleCA8LSBwZWFrX2RyYXdzX2FnZV9zZXggJT4lCiAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICBtZWFuX3FpKGVzdGltYXRlKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnBlYWsiKQoKCiNMZXZlbAogCm91dF9hZ2Vfc2V4XzIgPC0gY3Jvc3NpbmcobWRhdGFfYWdlX3NleCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeV9udW0sIGFnZSwgc2V4KSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA5KSwKICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikpCiAgCmxldmVsX2RyYXdzX2FnZV9zZXggPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXRfYWdlX3NleF8yLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSBtX2FnZV9zZXgpICU+JQogICAgYXJyYW5nZSh5X251bSwgLmRyYXcpICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoLmVwcmVkKS9maXJzdCguZXByZWQpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKICAKbGV2ZWxfc3VtbWFyeV9hZ2Vfc2V4IDwtIGxldmVsX2RyYXdzX2FnZV9zZXggJT4lCiAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICBtZWFuX3FpKGVzdGltYXRlKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKCiNTbG9wZQoKb3V0X2FnZV9zZXhfMyA8LSBjcm9zc2luZyhtZGF0YV9hZ2Vfc2V4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtICVpbiUgYyg5LDE0KSksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYy4gcG9zdC1hY2YiKSkKICAKc2xvcGVfZHJhd3NfYWdlX3NleCA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dF9hZ2Vfc2V4XzMsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IG1fYWdlX3NleCkgJT4lCiAgICAgICAgYXJyYW5nZSh5X251bSkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIGdyb3VwX2J5KC5kcmF3LCB5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgIHN1bW1hcmlzZShzbG9wZSA9IGxhc3QoLmVwcmVkKS9maXJzdCguZXByZWQpKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgICAgICBzdW1tYXJpc2UoZXN0aW1hdGUgPSBsYXN0KHNsb3BlKS9maXJzdChzbG9wZSkpICU+JQogICAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKICAKc2xvcGVfc3VtbWFyeV9hZ2Vfc2V4IDwtIHNsb3BlX2RyYXdzX2FnZV9zZXggJT4lCiAgICAgZ3JvdXBfYnkoYWdlLCBzZXgpICU+JQogICAgICBtZWRpYW5fcWkoZXN0aW1hdGUpICU+JQogICAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5zbG9wZSIpCgpgYGAKCgpOdW1lcmljYWwgc3VtbWFyeSBvZiB0aGVzZSBzdW1tYXJ5IHJlc3VsdHMKCmBgYHtyfQoKYmluZF9yb3dzKAogIHBlYWtfc3VtbWFyeV9hZ2Vfc2V4LCBsZXZlbF9zdW1tYXJ5X2FnZV9zZXgsIHNsb3BlX3N1bW1hcnlfYWdlX3NleAopICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKCgpgYGAKCkFzIGEgZmlndXJlCgpgYGB7cn0KCnBlYWtfZ19hZ2Vfc2V4IDwtIHBlYWtfc3VtbWFyeV9hZ2Vfc2V4ICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0iTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSJGIiB+ICJGZW1hbGUiKSkgJT4lCiAgbXV0YXRlKGFnZSA9IGNhc2Vfd2hlbihhZ2U9PSIwMF8wNSIgfiAiMCB0byA1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIwNl8xNSIgfiAiMDYgdG8gMTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjE2XzI1IiB+ICIxNiB0byAyNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMjZfMzUiIH4gIjI2IHRvIDM1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIzNl80NSIgfiAiMzYgdG8gNDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjQ2XzU1IiB+ICI0NiB0byA1NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNTZfNjUiIH4gIjU2IHRvIDY1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI2NSsiIH4gIjY1ICYgdXAgeSIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0xKSwgbGluZXR5cGU9MikrCiAgZ2VvbV9wb2ludHJhbmdlKGFlcyh4PWFnZSwgeT1lc3RpbWF0ZSwgeW1pbj0ubG93ZXIsIHltYXg9LnVwcGVyLCBncm91cD1zZXgsIGNvbG91cj1zZXgsIHNoYXBlPXNleCksCiAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjUpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjQ0Q3QUM1IiwgImNhZGV0Ymx1ZTMiKSwgbmFtZT0iIikgKwogIHNjYWxlX3NoYXBlKG5hbWU9IiIpICsKICBsYWJzKHg9IiIsCiAgICAgICB5PSJSZWxhdGl2ZSByYXRlICg5NSUgVUkpIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQoKI2xldmVsIHBsb3QKbGV2ZWxfZ19hZ2Vfc2V4IDwtIGxldmVsX3N1bW1hcnlfYWdlX3NleCAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9MSksIGxpbmV0eXBlPTIpKwogIGdlb21fcG9pbnRyYW5nZShhZXMoeD1hZ2UsIHk9ZXN0aW1hdGUsIHltaW49Lmxvd2VyLCB5bWF4PS51cHBlciwgZ3JvdXA9c2V4LCBjb2xvdXI9c2V4LCBzaGFwZT1zZXgpLAogICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC41KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI0NEN0FDNSIsICJjYWRldGJsdWUzIiksIG5hbWU9IiIpICsKICBzY2FsZV9zaGFwZShuYW1lPSIiKSArCiAgbGFicyh4PSIiLAogICAgICAgeT0iUmVsYXRpdmUgcmF0ZSAoOTUlIFVJKSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCiNzbG9wZSBwbG90CnNsb3BlX2dfYWdlX3NleCA8LSBzbG9wZV9zdW1tYXJ5X2FnZV9zZXggJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSJNIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNleD09IkYiIH4gIkZlbWFsZSIpKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PTEpLCBsaW5ldHlwZT0yKSsKICBnZW9tX3BvaW50cmFuZ2UoYWVzKHg9YWdlLCB5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIGdyb3VwPXNleCwgY29sb3VyPXNleCwgc2hhcGU9c2V4KSwKICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgc2NhbGVfc2hhcGUobmFtZT0iIikgKwogIGxhYnMoeD0iIiwKICAgICAgIHk9IlJlbGF0aXZlIHJhdGUgKDk1JSBVSSkiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgoKYGBgCgoKIyMjIyA5LjMgQ29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwKCmBgYHtyfQoKY291bnRlcmZhY3RfYWdlX3NleCA8LQogICAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0gbV9hZ2Vfc2V4LAogICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IG1kYXRhX2FnZV9zZXggJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIGFnZSwgc2V4KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGFjZl9wZXJpb2QgPSAiYS4gcHJlLWFjZiIpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHNlbGVjdCh5ZWFyLCBhZ2UsIHNleCwgLmRyYXcsIC5lcHJlZF9jb3VudGVyZiA9IC5lcHJlZCkKICAKI0NhbGN1YXRlIHByZWRpY3RlZCBudW1iZXIgb2YgY2FzZXMgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgpwb3N0X2NoYW5nZV9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgsIGFjZl9wZXJpb2QpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgc2VsZWN0KHllYXIsIGFnZSwgc2V4LCAuZHJhdywgLmVwcmVkKSAKICAKI2ZvciB0aGUgb3ZlcmFsbCBwZXJpb2QKY291bnRlcmZhY3Rfb3ZlcmFsbF9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIikpICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgc2VsZWN0KGFnZSwgc2V4LCAuZHJhdywgLmVwcmVkKSAgJT4lCiAgICAgIGdyb3VwX2J5KGFnZSwgc2V4LCAuZHJhdykgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWRfY291bnRlcmYgPSBzdW0oLmVwcmVkKSkgJT4lCiAgICAgIG11dGF0ZSh5ZWFyID0gIk92ZXJhbGwgKDE5NTgtMTk2MykiKQogIAojQ2FsY3VhdGUgaW5jaWRlbmNlIHBlciBkcmF3LCB0aGVuIHN1bW1hcmlzZS4KcG9zdF9jaGFuZ2Vfb3ZlcmFsbF9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgsIGFjZl9wZXJpb2QpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHNlbGVjdChhZ2UsIHNleCwgLmRyYXcsIC5lcHJlZCkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCBhZ2UsIHNleCkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWQgPSBzdW0oLmVwcmVkKSkgCiAgCmNvdW50ZXJfcG9zdF9vdmVyYWxsX2FnZV9zZXggPC0KICBsZWZ0X2pvaW4oY291bnRlcmZhY3Rfb3ZlcmFsbF9hZ2Vfc2V4LCBwb3N0X2NoYW5nZV9vdmVyYWxsX2FnZV9zZXgpICU+JQogICAgbXV0YXRlKGNhc2VzX2F2ZXJ0ZWQgPSAuZXByZWRfY291bnRlcmYtLmVwcmVkLAogICAgICAgICAgIHBjdF9jaGFuZ2UgPSAoLmVwcmVkIC0gLmVwcmVkX2NvdW50ZXJmKS8uZXByZWRfY291bnRlcmYpICU+JQogICAgZ3JvdXBfYnkoYWdlLCBzZXgpICU+JQogICAgbWVhbl9xaShjYXNlc19hdmVydGVkLCBwY3RfY2hhbmdlKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gIk92ZXJhbGwgKDE5NTgtMTk2MykiKSAKCgphZ2Vfc2V4X3R4dCA8LSBjb3VudGVyX3Bvc3Rfb3ZlcmFsbF9hZ2Vfc2V4ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgdHJhbnNtdXRlKHllYXIgPSBhcy5jaGFyYWN0ZXIoeWVhciksCiAgICAgICAgICAgIHNleCA9IHNleCwKICAgICAgICAgICAgYWdlID0gYWdlLAogICAgICAgICAgICBjYXNlc19hdmVydGVkID0gZ2x1ZTo6Z2x1ZSgie2Nhc2VzX2F2ZXJ0ZWR9XG4oe2Nhc2VzX2F2ZXJ0ZWQubG93ZXJ9IHRvIHtjYXNlc19hdmVydGVkLnVwcGVyfSkiKSwKICAgICAgICAgICAgcGN0X2NoYW5nZSA9IGdsdWU6OmdsdWUoIntwY3RfY2hhbmdlfVxuKHtwY3RfY2hhbmdlLmxvd2VyfSB0byB7cGN0X2NoYW5nZS51cHBlcn0pIikpCgoKYWdlX3NleF90eHQgJT4lIGRhdGF0YWJsZSgpCgoKYGBgCgpgYGB7cn0KCmNvdW50ZXJmYWN0dWFsX2dfYWdlX3NleCA8LSBjb3VudGVyX3Bvc3Rfb3ZlcmFsbF9hZ2Vfc2V4ICU+JSAKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnRyYW5nZShhZXMoeCA9IGFnZSwgeT1jYXNlc19hdmVydGVkLCB5bWluPWNhc2VzX2F2ZXJ0ZWQubG93ZXIsIHltYXg9Y2FzZXNfYXZlcnRlZC51cHBlciwgY29sb3VyPXNleCwgc2hhcGU9c2V4KSwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9MC41KSkgKyAKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgc2NhbGVfc2hhcGUobmFtZT0iIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGxhYnMoeD0iIiwKICAgICAgIHk9Ik51bWJlciAoOTUlIFVJKSIsCiAgICAgICBjb2xvdXI9IiIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmNvdW50ZXJmYWN0dWFsX2dfYWdlX3NleApgYGAKCkpvaW4gdG9nZXRoZXIgZm9yIEZpZ3VyZSAzLgoKCmBgYHtyfQoKKHBlYWtfZ19hZ2Vfc2V4ICsgbGV2ZWxfZ19hZ2Vfc2V4KSAvIChzbG9wZV9nX2FnZV9zZXggKyBjb3VudGVyZmFjdHVhbF9nX2FnZV9zZXgpICsgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAiQSIpICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSAmIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZjMudGlmZiIpLCB3aWR0aCA9IDEyLCBoZWlnaHQ9OCkKCgpgYGAKCgo=